diff --git a/docs/settingup.md b/docs/settingup.md
index 8f16259b..51021bb1 100644
--- a/docs/settingup.md
+++ b/docs/settingup.md
@@ -2261,6 +2261,30 @@ Unsubscribe from all currently subscribed objects.
```
+
+stepdimension
+
+## StepDimension action
+
+Cycle a step in a cyclic dimension
+
+* `id`: library ID of the cyclic dimension
+
+### Example
+
+Cycle one step in the dimension with library ID `aBc123`.
+
+```json
+{
+ "action": "stepdimension",
+ "settings":{
+ "id": "aBc123"
+ }
+}
+```
+
+
+
diff --git a/generatedocs/data/actions/stepdimension/description.md b/generatedocs/data/actions/stepdimension/description.md
new file mode 100644
index 00000000..650abcc0
--- /dev/null
+++ b/generatedocs/data/actions/stepdimension/description.md
@@ -0,0 +1,3 @@
+## StepDimension action
+
+Cycle a step in a cyclic dimension
diff --git a/generatedocs/data/actions/stepdimension/examples.md b/generatedocs/data/actions/stepdimension/examples.md
new file mode 100644
index 00000000..f86cf9a0
--- /dev/null
+++ b/generatedocs/data/actions/stepdimension/examples.md
@@ -0,0 +1,12 @@
+### Example
+
+Cycle one step in the dimension with library ID `aBc123`.
+
+```json
+{
+ "action": "stepdimension",
+ "settings":{
+ "id": "aBc123"
+ }
+}
+```
diff --git a/generatedocs/data/groups/groups.json b/generatedocs/data/groups/groups.json
index 190f2efe..bf6e9117 100644
--- a/generatedocs/data/groups/groups.json
+++ b/generatedocs/data/groups/groups.json
@@ -38,7 +38,8 @@
"thinktime",
"unpublishbookmark",
"unpublishsheet",
- "unsubscribeobjects"
+ "unsubscribeobjects",
+ "stepdimension"
]
},
{
diff --git a/generatedocs/data/params.json b/generatedocs/data/params.json
index 3cfda0f8..8cb92c98 100644
--- a/generatedocs/data/params.json
+++ b/generatedocs/data/params.json
@@ -558,6 +558,9 @@
"setscriptvar.value": [
"Value to set to variable (supports the use of [session variables](#session_variables))."
],
+ "stepdimension.id": [
+ "library ID of the cyclic dimension"
+ ],
"subscribeobjects.clear" : [
"Remove any previously subscribed objects from the subscription list."
],
diff --git a/generatedocs/generated/documentation.go b/generatedocs/generated/documentation.go
index 0a88f56a..71326c1e 100644
--- a/generatedocs/generated/documentation.go
+++ b/generatedocs/generated/documentation.go
@@ -150,6 +150,10 @@ var (
Description: "## SmartSearch action\n\nPerform a Smart Search in Sense app to find suggested selections.\n",
Examples: "\n### Examples\n\n#### Search with one search term\n```json\n{\n \"action\": \"smartsearch\",\n \"label\": \"one term search\",\n \"settings\": {\n \"searchtextlist\": [\n \"term1\"\n ]\n }\n}\n```\n\n#### Search with two search terms\n```json\n{\n \"action\": \"smartsearch\",\n \"label\": \"two term search\",\n \"settings\": {\n \"searchtextlist\": [\n \"term1 term2\"\n ]\n }\n}\n```\n\n#### Search with random selection of search text from list\n```json\n{\n \"action\": \"smartsearch\",\n \"settings\": {\n \"searchtextlist\": [\n \"text1\",\n \"text2\",\n \"text3\"\n ]\n }\n}\n```\n\n#### Search with random selection of search text from file\n```json\n{\n \"action\": \"smartsearch\",\n \"settings\": {\n \"searchtextsource\": \"searchtextfile\",\n \"searchtextfile\": \"data/searchtexts.txt\"\n }\n}\n```\n##### `data/searchtexts.txt`\n```\nsearch text\n\"quoted search text\"\nanother search text\n```\n\n#### Simulate pasting search text\n\nThe default behavior is to simulate typing at normal speed.\n```json\n{\n \"action\": \"smartsearch\",\n \"settings\": {\n \"pastesearchtext\": true,\n \"searchtextlist\": [\n \"text1\"\n ]\n }\n}\n```\n\n#### Make a random selection from search results\n```json\n{\n \"action\": \"smartsearch\",\n \"settings\": {\n \"searchtextlist\": [\n \"term1\"\n ],\n \"makeselection\": true,\n \"selectionthinktime\": {\n \"type\": \"static\",\n \"delay\": 2\n }\n }\n}\n```\n\n#### Search with one search term including spaces\n```json\n{\n \"action\": \"smartsearch\",\n \"settings\": {\n \"searchtextlist\": [\n \"\\\"word1 word2\\\"\"\n ]\n }\n}\n```\n\n#### Search with two search terms, one of them including spaces\n```json\n{\n \"action\": \"smartsearch\",\n \"label\": \"two term search, one including spaces\",\n \"settings\": {\n \"searchtextlist\": [\n \"\\\"word1 word2\\\" term2\"\n ]\n }\n}\n```\n\n#### Search with one search term including double quote\n```json\n{\n \"action\": \"smartsearch\",\n \"label\": \"one term search including spaces\",\n \"settings\": {\n \"searchtext\":\n \"searchtextlist\": [\n \"\\\\\\\"hello\"\n ]\n }\n}\n```\n",
},
+ "stepdimension": {
+ Description: "## StepDimension action\n\nCycle a step in a cyclic dimension\n",
+ Examples: "### Example\n\nCycle one step in the dimension with library ID `aBc123`.\n\n```json\n{\n \"action\": \"stepdimension\",\n \"settings\":{\n \"id\": \"aBc123\"\n }\n}\n```\n",
+ },
"subscribeobjects": {
Description: "## Subscribeobjects action\n\nSubscribe to any object in the currently active app.\n",
Examples: "### Example\n\nSubscribe to two objects in the currently active app and remove any previous subscriptions. \n\n```json\n{\n \"action\" : \"subscribeobjects\",\n \"label\" : \"clear subscriptions and subscribe to mBshXB and f2a50cb3-a7e1-40ac-a015-bc4378773312\",\n \"disabled\": false,\n \"settings\" : {\n \"clear\" : true,\n \"ids\" : [\"mBshXB\", \"f2a50cb3-a7e1-40ac-a015-bc4378773312\"]\n }\n}\n```\n\nSubscribe to an additional single object (or a list of objects) in the currently active app, adding the new subscription to any previous subscriptions.\n\n```json\n{\n \"action\" : \"subscribeobjects\",\n \"label\" : \"add c430d8e2-0f05-49f1-aa6f-7234e325dc35 to currently subscribed objects\",\n \"disabled\": false,\n \"settings\" : {\n \"clear\" : false,\n \"ids\" : [\"c430d8e2-0f05-49f1-aa6f-7234e325dc35\"]\n }\n}\n```",
@@ -349,6 +353,7 @@ var (
"smartsearch.searchtextlist": {"List of of strings used for searching."},
"smartsearch.searchtextsource": {"Source for list of strings used for searching.", "`searchtextlist` (default)", "`searchtextfile`"},
"smartsearch.selectionthinktime": {"Think time before selection if `makeselection` is `true`, defaults to a 1 second delay."},
+ "stepdimension.id": {"library ID of the cyclic dimension"},
"subscribeobjects.clear": {"Remove any previously subscribed objects from the subscription list."},
"subscribeobjects.ids": {"List of object IDs to subscribe to."},
"thinktime.delay": {"Delay (seconds), used with type `static`."},
@@ -399,7 +404,7 @@ var (
{
Name: "commonActions",
Title: "Common actions",
- Actions: []string{"applybookmark", "askhubadvisor", "changesheet", "clearall", "clearfield", "clickactionbutton", "containertab", "createbookmark", "createsheet", "deletebookmark", "deletesheet", "disconnectapp", "disconnectenvironment", "dosave", "duplicatesheet", "getscript", "iterated", "listboxselect", "objectsearch", "openapp", "productversion", "publishbookmark", "publishsheet", "randomaction", "reload", "select", "setscript", "setscriptvar", "setsensevariable", "sheetchanger", "smartsearch", "subscribeobjects", "thinktime", "unpublishbookmark", "unpublishsheet", "unsubscribeobjects"},
+ Actions: []string{"applybookmark", "askhubadvisor", "changesheet", "clearall", "clearfield", "clickactionbutton", "containertab", "createbookmark", "createsheet", "deletebookmark", "deletesheet", "disconnectapp", "disconnectenvironment", "dosave", "duplicatesheet", "getscript", "iterated", "listboxselect", "objectsearch", "openapp", "productversion", "publishbookmark", "publishsheet", "randomaction", "reload", "select", "setscript", "setscriptvar", "setsensevariable", "sheetchanger", "smartsearch", "subscribeobjects", "thinktime", "unpublishbookmark", "unpublishsheet", "unsubscribeobjects", "stepdimension"},
DocEntry: common.DocEntry{
Description: "# Common actions\n\nThese actions are applicable for most types of Qlik Sense deployments.\n\n**Note:** It is recommended to prepend the actions listed here with an `openapp` action as most of them perform operations in an app context (such as making selections or changing sheets).\n",
Examples: "",
diff --git a/globals/constant/sense.go b/globals/constant/sense.go
index ed4e8e21..7c8ec190 100644
--- a/globals/constant/sense.go
+++ b/globals/constant/sense.go
@@ -38,3 +38,10 @@ const (
DataReductionModeClustered = "C"
DataReductionModeStacked = "ST"
)
+
+// NxDimensionInfoGrouping
+const (
+ NxDimensionInfoGroupingNone = "N"
+ NxDimensionInfoGroupingHiearchy = "H"
+ NxDimensionInfoGroupingCollection = "C"
+)
diff --git a/scenario/actionhandler.go b/scenario/actionhandler.go
index bc64a9ee..c20428c8 100644
--- a/scenario/actionhandler.go
+++ b/scenario/actionhandler.go
@@ -148,6 +148,7 @@ const (
ActionObjectSearch = "objectsearch"
ActionGetScript = "getscript"
ActionChangeSteam = "changestream"
+ ActionStepDimension = "stepdimension"
)
// Scenario actions needs an entry in actionHandler
@@ -260,6 +261,7 @@ func ResetDefaultActions() {
ActionObjectSearch: ObjectSearchSettings{},
ActionGetScript: GetscriptSettings{},
ActionChangeSteam: ChangestreamSettings{},
+ ActionStepDimension: StepDimensionSettings{},
}
}
diff --git a/scenario/stepdimension.go b/scenario/stepdimension.go
new file mode 100644
index 00000000..c9ccec6c
--- /dev/null
+++ b/scenario/stepdimension.go
@@ -0,0 +1,54 @@
+package scenario
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+ "github.com/qlik-oss/gopherciser/action"
+ "github.com/qlik-oss/gopherciser/connection"
+ "github.com/qlik-oss/gopherciser/session"
+)
+
+type (
+ // StepDimensionSettings cycle a step in a cyclic dimension
+ StepDimensionSettings struct {
+ Id string `json:"id" doc-key:"stepdimension.id"`
+ }
+)
+
+// Validate StepDimensionSettings action (Implements ActionSettings interface)
+func (settings StepDimensionSettings) Validate() ([]string, error) {
+ if settings.Id == "" {
+ return nil, errors.Errorf("Id not set for %s", ActionStepDimension)
+ }
+ return nil, nil
+}
+
+// Execute StepDimensionSettings action (Implements ActionSettings interface)
+func (settings StepDimensionSettings) Execute(sessionState *session.State, actionState *action.State, connection *connection.ConnectionSettings, label string, reset func()) {
+ if sessionState.Connection == nil || sessionState.Connection.Sense() == nil {
+ actionState.AddErrors(errors.New("Not connected to a Sense environment"))
+ return
+ }
+
+ app := sessionState.Connection.Sense().CurrentApp
+ if app == nil {
+ actionState.AddErrors(errors.New("Not connected to a Sense app"))
+ return
+ }
+
+ dim, err := app.GetDimension(sessionState, actionState, settings.Id)
+ if err != nil {
+ actionState.AddErrors(err)
+ return
+ }
+
+ err = sessionState.SendRequest(actionState, func(ctx context.Context) error {
+ return dim.StepCycle(ctx, 1)
+ })
+ if err != nil {
+ actionState.AddErrors(err)
+ }
+
+ sessionState.Wait(actionState) // Await all async requests, e.g. those triggered on changed objects
+}
diff --git a/scripts/lint.sh b/scripts/lint.sh
index 2c02e000..60e54087 100755
--- a/scripts/lint.sh
+++ b/scripts/lint.sh
@@ -3,24 +3,6 @@
# Purpose: This script lints the code.
# Instructions: make lint
-# On darwin you can also install with homebrew
-# brew install golangci/tap/golangci-lint
-
-# set OS
-OS=
-if [[ "$OSTYPE" == "linux-gnu" ]]; then
- OS=Linux
-elif [[ "$OSTYPE" == "darwin"* ]]; then
- # Mac OSX
- OS=Darwin
-elif [[ "$OSTYPE" == "cygwin" ]]; then
- # POSIX compatibility layer and Linux environment emulation for Windows
- OS=Windows
-else
- # Unknown, assume Linux
- OS=Linux
-fi
-
# set lint level
LINTLEVEL=${1:-DEFAULT}
@@ -48,15 +30,12 @@ fi
echo Running lint
case $LINTLEVEL in
# minimal amount of linting currently running clean. More linters and and rules will be added as more lint errors are fixed.
-# Current status:
-# linters currently looked at: govet
-# subrules currently disabled:
-# * structtag : We override tags in engima, so structtag complains about repeating tags, found no way to tell it this is intended.
+# currently no subrules disabled in "min"
MIN)
- "$GOPATH"/bin/golangci-lint run -D structcheck --timeout 5m
+ "$GOPATH"/bin/golangci-lint run --timeout 5m
;;
-# Default set of linters
+# "full" set of linters
*)
- "$GOPATH"/bin/golangci-lint run
+ "$GOPATH"/bin/golangci-lint run --timeout 5m
;;
esac
diff --git a/senseobjects/app.go b/senseobjects/app.go
index fe32f3ca..1a386367 100644
--- a/senseobjects/app.go
+++ b/senseobjects/app.go
@@ -22,6 +22,7 @@ type (
loadmodellist *LoadModelList
fieldlist *FieldList
dimensionList *DimensionList
+ dimensions map[string]*enigma.GenericDimension
appPropsList *AppPropsList
}
@@ -542,3 +543,35 @@ func (app *App) setDimensionList(SessionState SessionState, dl *DimensionList) {
}
app.dimensionList = dl
}
+
+func (app *App) GetDimension(sessionState SessionState, actionState *action.State, id string) (*enigma.GenericDimension, error) {
+ dim := app.dimensions[id]
+ if dim != nil {
+ return dim, nil
+ }
+
+ err := sessionState.SendRequest(actionState, func(ctx context.Context) error {
+ var err error
+ dim, err = app.Doc.GetDimension(ctx, id)
+ return err
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ app.addDimension(dim)
+
+ return dim, nil
+}
+
+func (app *App) addDimension(dim *enigma.GenericDimension) {
+ app.mutex.Lock()
+ defer app.mutex.Unlock()
+
+ if app.dimensions == nil {
+ app.dimensions = map[string]*enigma.GenericDimension{dim.GenericId: dim}
+ return
+ }
+
+ app.dimensions[dim.GenericId] = dim
+}
diff --git a/session/objecthandling.go b/session/objecthandling.go
index f315e25c..9443662d 100644
--- a/session/objecthandling.go
+++ b/session/objecthandling.go
@@ -344,7 +344,7 @@ func SetListObject(rawLayout json.RawMessage, obj *enigmahandlers.Object, path h
return nil
}
-func SetHyperCube(rawLayout json.RawMessage, obj *enigmahandlers.Object, path helpers.DataPath) error {
+func SetHyperCube(sessionState *State, actionState *action.State, rawLayout json.RawMessage, obj *enigmahandlers.Object, path helpers.DataPath) error {
rawHyperCube, err := path.Lookup(rawLayout)
if err != nil {
return errors.Wrap(err, "error getting hypercube")
@@ -356,6 +356,27 @@ func SetHyperCube(rawLayout json.RawMessage, obj *enigmahandlers.Object, path he
}
obj.SetHyperCube(hyperCube)
+
+ // Look for cyclic dimensions and add to app sessionobjects
+ if len(hyperCube.DimensionInfo) > 0 {
+ for i, dim := range hyperCube.DimensionInfo {
+ if dim != nil && dim.Grouping == constant.NxDimensionInfoGroupingCollection {
+ app, err := sessionState.CurrentSenseApp()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ if dim.LibraryId == "" {
+ sessionState.LogEntry.Logf(logger.WarningLevel, "object<%s> dim<%d> has grouping, but no library ID", obj.ID, i)
+ continue
+ }
+ // GetDimension (adds it to sessionobjects list)
+ if _, err = app.GetDimension(sessionState, actionState, dim.LibraryId); err != nil {
+ actionState.AddErrors(err)
+ }
+ }
+ }
+ }
+
return nil
}
@@ -888,7 +909,7 @@ func SetObjectData(sessionState *State, actionState *action.State, rawLayout jso
return errors.Errorf(
"object<%s> is defined as hypercube carrier, but has not hypercube path definition", enigmaObject.GenericType)
}
- if err := SetHyperCube(rawLayout, obj, objectDef.DataDef.Path); err != nil {
+ if err := SetHyperCube(sessionState, actionState, rawLayout, obj, objectDef.DataDef.Path); err != nil {
return errors.Wrapf(err, "object<%s> type<%s>", obj.ID, enigmaObject.GenericType)
}
default: