From aaa5ab077ec5b861265fdc5e8752cca388fb1ada Mon Sep 17 00:00:00 2001 From: Daniel Larsson Date: Fri, 31 Jan 2025 12:46:27 +0100 Subject: [PATCH 1/6] support for nl insights with added measures and dimensions --- config/config.go | 2 +- scheduler/scheduler.go | 6 +- senseobjdef/defaultdefinitions.go | 25 +++++- session/narrativeshandler.go | 145 ++++++++++++++++++++++++++++++ session/objecthandling.go | 3 + session/resthandler.go | 28 ++++-- 6 files changed, 196 insertions(+), 13 deletions(-) create mode 100644 session/narrativeshandler.go diff --git a/config/config.go b/config/config.go index 32a7445a..bf49be0b 100644 --- a/config/config.go +++ b/config/config.go @@ -578,7 +578,7 @@ func (cfg *Config) TestConnection(ctx context.Context) error { if err != nil { return errors.Wrap(err, "failed to set up REST client") } - sessionState.Rest.SetClient(client) + sessionState.Rest.SetClient(client, "", "") actionState := &action.State{} sessionState.CurrentActionState = actionState diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 02ecec76..f6af9902 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -297,7 +297,11 @@ func setupRESTHandler(sessionState *session.State, connectionSettings *connectio return errors.WithStack(err) } - sessionState.Rest.SetClient(client) + restProtocol := "http://" + if connectionSettings.Security { + restProtocol = "https://" + } + sessionState.Rest.SetClient(client, host, restProtocol) return nil } diff --git a/senseobjdef/defaultdefinitions.go b/senseobjdef/defaultdefinitions.go index 23fb54a5..96a4cdb2 100644 --- a/senseobjdef/defaultdefinitions.go +++ b/senseobjdef/defaultdefinitions.go @@ -39,7 +39,7 @@ var ( DataDef{DataDefHyperCube, "/qHyperCube"}, []Data{ {DataCore{ - []*Constraint{&Constraint{ + []*Constraint{{ Path: "/qHyperCube/qSize/qcy", Value: ">1000", Required: true, @@ -132,12 +132,12 @@ var ( []Data{ {DataCore{ []*Constraint{ - &Constraint{ + { Path: "/preferContinuousAxis", Value: "=true", Required: false, }, - &Constraint{ + { Path: "/qHyperCube/qDimensionInfo/[0]/qTags", Value: "~$numeric", Required: false, @@ -774,6 +774,24 @@ var ( nil, } + DefaultSnNlgChart = ObjectDef{ + DataDef: DataDef{ + Type: DataDefHyperCube, + Path: "/qHyperCube", + }, + Data: []Data{ + { + DataCore{ + Requests: []GetDataRequests{ + { + Type: DataTypeLayout, + }, + }, + }, + }, + }, + } + DefaultObjectDefs = ObjectDefs{ "listbox": &DefaultListboxDef, "filterpane": &DefaultFilterpane, @@ -821,5 +839,6 @@ var ( "sn-pivot-table": &DefaultSNPivotTable, "sn-layout-container": &DefaultLayoutContainer, "sn-tabbed-container": &DefaultTabbedContainer, + "sn-nlg-chart": &DefaultSnNlgChart, } ) diff --git a/session/narrativeshandler.go b/session/narrativeshandler.go new file mode 100644 index 00000000..aef5b9d3 --- /dev/null +++ b/session/narrativeshandler.go @@ -0,0 +1,145 @@ +package session + +import ( + "context" + "encoding/json" + "fmt" + "sync" + + "github.com/pkg/errors" + "github.com/qlik-oss/enigma-go/v4" + "github.com/qlik-oss/gopherciser/action" + "github.com/qlik-oss/gopherciser/enigmahandlers" + "github.com/qlik-oss/gopherciser/logger" + "github.com/qlik-oss/gopherciser/senseobjdef" +) + +type ( + NarrativesHandler struct{} + NarrativesHandlerInstance struct { + ID string + } + + NarrativesPayloadExpressionOverrides struct { + Classifications []string `json:"classifications"` + Format *enigma.FieldAttributes `json:"format,omitempty"` + } + + NarrativesPayloadExpression struct { + Expr string `json:"expr"` + Label string `json:"label"` + Overrides NarrativesPayloadExpressionOverrides `json:"overrides"` + } + + NarrativesPayload struct { + AlternateStateName string `json:"alternateStateName"` + AnalysisTypes []interface{} `json:"analysisTypes"` + AppID string `json:"appId"` + Expressions []NarrativesPayloadExpression `json:"expressions"` + Fields []interface{} `json:"fields"` + Lang string `json:"lang"` + LibItems []interface{} `json:"libItems"` + Verbosity string `json:"verbosity"` + } +) + +// Instance implements ObjectHandler interface +func (handler *NarrativesHandler) Instance(id string) ObjectHandlerInstance { + return &NarrativesHandlerInstance{ID: id} +} + +// GetObjectDefinition implements ObjectHandlerInstance interface +func (handler *NarrativesHandlerInstance) GetObjectDefinition(objectType string) (string, senseobjdef.SelectType, senseobjdef.DataDefType, error) { + if objectType != "sn-nlg-chart" { + return "", senseobjdef.SelectTypeUnknown, senseobjdef.DataDefUnknown, errors.New("NarrativesHandlerInstance only handles objects of type sn-nlg-chart") + } + return (&DefaultHandlerInstance{}).GetObjectDefinition("sn-nlg-chart") +} + +// SetObjectAndEvents implements ObjectHandlerInstance interface +func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State, actionState *action.State, obj *enigmahandlers.Object, genObj *enigma.GenericObject) { + var wg sync.WaitGroup + + wg.Add(1) + sessionState.QueueRequest(func(ctx context.Context) error { + defer wg.Done() + return GetObjectProperties(sessionState, actionState, obj) + }, actionState, true, "") + + wg.Add(1) + sessionState.QueueRequest(func(ctx context.Context) error { + defer wg.Done() + return GetObjectLayout(sessionState, actionState, obj, nil) + }, actionState, true, "") + + if sessionState.Rest == nil { + sessionState.LogEntry.Log(logger.WarningLevel, "no resthandler defined, nl insights object will not generated correctly") + return + } + + wg.Wait() + + app := sessionState.CurrentApp + if app == nil || app.ID == "" { + actionState.AddErrors(errors.Errorf("no current app found")) + return + } + + // TODO check for library id's? + expressions := make([]NarrativesPayloadExpression, len(obj.Properties().HyperCubeDef.Measures)) + for i, measure := range obj.Properties().HyperCubeDef.Measures { + expressions[i] = NarrativesPayloadExpression{ + Expr: measure.Def.Def, + Overrides: NarrativesPayloadExpressionOverrides{ + Classifications: []string{"measure"}, + Format: measure.Def.NumFormat, + }, + Label: "", + } + } + + for _, dimension := range obj.Properties().HyperCubeDef.Dimensions { + expressions = append(expressions, NarrativesPayloadExpression{ + Expr: dimension.Def.FieldDefs[0], + Overrides: NarrativesPayloadExpressionOverrides{ + Classifications: []string{"dimension"}, + }, + Label: "", + }) + } + + payload := NarrativesPayload{ + AppID: app.ID, + Lang: "en", + Verbosity: "full", + Expressions: expressions, + AnalysisTypes: nil, + Fields: nil, + LibItems: nil, + } + + if obj.HyperCube().StateName != "" && obj.HyperCube().StateName != "$" { + payload.AlternateStateName = obj.HyperCube().StateName + } + + content, err := json.Marshal(payload) + if err != nil { + actionState.AddErrors(errors.Wrap(err, "failed to marshal narratives payload")) + return + } + + protocol := sessionState.Rest.Protocol() + host := sessionState.Rest.Host() + + sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) + + event := func(ctx context.Context, as *action.State) error { + if err := GetObjectLayout(sessionState, as, obj, nil); err != nil { + return err + } + sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) + return nil + } + + sessionState.RegisterEvent(genObj.Handle, event, nil, true) +} diff --git a/session/objecthandling.go b/session/objecthandling.go index fb142208..925fe1cb 100644 --- a/session/objecthandling.go +++ b/session/objecthandling.go @@ -73,6 +73,9 @@ func init() { if err := GlobalObjectHandler.RegisterHandler("sn-tabbed-container", &TabbedContainerHandler{}, false); err != nil { panic(err) } + if err := GlobalObjectHandler.RegisterHandler("sn-nlg-chart", &NarrativesHandler{}, false); err != nil { + panic(err) + } } func (err NxValidationError) Error() string { diff --git a/session/resthandler.go b/session/resthandler.go index 480f175e..b5bded93 100644 --- a/session/resthandler.go +++ b/session/resthandler.go @@ -40,13 +40,15 @@ type ( // RestHandler handles waiting for pending requests and responses RestHandler struct { - timeout time.Duration - Client *http.Client - trafficLogger enigma.TrafficLogger - headers *HeaderJar - virtualProxy string - ctx context.Context - pending *pending.Handler + timeout time.Duration + Client *http.Client + trafficLogger enigma.TrafficLogger + headers *HeaderJar + virtualProxy string + ctx context.Context + pending *pending.Handler + defaultHost string + defaultProtocol string } // RestRequest represents a REST request and its response @@ -308,8 +310,18 @@ func DefaultReqOptions() *ReqOptions { } // SetClient set HTTP client for this RestHandler -func (handler *RestHandler) SetClient(client *http.Client) { +func (handler *RestHandler) SetClient(client *http.Client, defaultHost, defaultProtocol string) { handler.Client = client + handler.defaultHost = defaultHost + handler.defaultProtocol = defaultProtocol +} + +func (handler *RestHandler) Host() string { + return handler.defaultHost +} + +func (handler *RestHandler) Protocol() string { + return handler.defaultProtocol } // GetSync sends synchronous GET request with options, using options=nil default options are used From 8ecb1664f552bd08b8da6d3fee8d2b5a2491cb2d Mon Sep 17 00:00:00 2001 From: Daniel Larsson Date: Fri, 31 Jan 2025 13:09:48 +0100 Subject: [PATCH 2/6] fix linting issues --- session/narrativeshandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/session/narrativeshandler.go b/session/narrativeshandler.go index aef5b9d3..b97e0d7e 100644 --- a/session/narrativeshandler.go +++ b/session/narrativeshandler.go @@ -131,13 +131,13 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State protocol := sessionState.Rest.Protocol() host := sessionState.Rest.Host() - sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) + _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) event := func(ctx context.Context, as *action.State) error { if err := GetObjectLayout(sessionState, as, obj, nil); err != nil { return err } - sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) + _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) return nil } From ccdd7f530318d2b5d20b00af81f3cb6ba96f6fb3 Mon Sep 17 00:00:00 2001 From: Daniel Larsson Date: Mon, 3 Feb 2025 14:21:56 +0100 Subject: [PATCH 3/6] support nlg specific internal object --- session/narrativeshandler.go | 108 ++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 20 deletions(-) diff --git a/session/narrativeshandler.go b/session/narrativeshandler.go index b97e0d7e..46e4fa46 100644 --- a/session/narrativeshandler.go +++ b/session/narrativeshandler.go @@ -15,9 +15,24 @@ import ( ) type ( + NarrativesPropertiesNlgChartObject struct { + ChartObjectId string `json:"chartObjectId"` + Label string `json:"label"` + Type string `json:"type"` + ExtendsId string `json:"qExtendsId"` + Dimensions []*enigma.NxDimension `json:"qDimensions"` + Measures []*enigma.NxMeasure `json:"qMeasures"` + } + + NarrativesProperties struct { + *enigma.GenericObjectProperties + NlgChartObject *NarrativesPropertiesNlgChartObject `json:"nlgChartObject"` + } + NarrativesHandler struct{} NarrativesHandlerInstance struct { - ID string + ID string + Properties *NarrativesProperties } NarrativesPayloadExpressionOverrides struct { @@ -31,6 +46,11 @@ type ( Overrides NarrativesPayloadExpressionOverrides `json:"overrides"` } + NarrativesPayloadLibItem struct { + LibId string `json:"libId"` + Overrides interface{} `json:"overrides"` + } + NarrativesPayload struct { AlternateStateName string `json:"alternateStateName"` AnalysisTypes []interface{} `json:"analysisTypes"` @@ -38,7 +58,7 @@ type ( Expressions []NarrativesPayloadExpression `json:"expressions"` Fields []interface{} `json:"fields"` Lang string `json:"lang"` - LibItems []interface{} `json:"libItems"` + LibItems []NarrativesPayloadLibItem `json:"libItems"` Verbosity string `json:"verbosity"` } ) @@ -60,10 +80,12 @@ func (handler *NarrativesHandlerInstance) GetObjectDefinition(objectType string) func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State, actionState *action.State, obj *enigmahandlers.Object, genObj *enigma.GenericObject) { var wg sync.WaitGroup + fmt.Println("handle object: ", obj.ID) + wg.Add(1) sessionState.QueueRequest(func(ctx context.Context) error { defer wg.Done() - return GetObjectProperties(sessionState, actionState, obj) + return handler.GetNarrativesProperties(sessionState, actionState, obj) }, actionState, true, "") wg.Add(1) @@ -85,21 +107,53 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State return } - // TODO check for library id's? - expressions := make([]NarrativesPayloadExpression, len(obj.Properties().HyperCubeDef.Measures)) - for i, measure := range obj.Properties().HyperCubeDef.Measures { - expressions[i] = NarrativesPayloadExpression{ + payload := NarrativesPayload{ + AppID: app.ID, + Lang: "en", + Verbosity: "full", + Expressions: []NarrativesPayloadExpression{}, + AnalysisTypes: []interface{}{}, + Fields: []interface{}{}, + LibItems: []NarrativesPayloadLibItem{}, + } + + measures := handler.Properties.HyperCubeDef.Measures + if handler.Properties.NlgChartObject != nil { + measures = handler.Properties.NlgChartObject.Measures + } + + for _, measure := range measures { + if measure.LibraryId != "" { + payload.LibItems = append(payload.LibItems, NarrativesPayloadLibItem{ + LibId: measure.LibraryId, + Overrides: struct{}{}, + }) + continue + } + payload.Expressions = append(payload.Expressions, NarrativesPayloadExpression{ Expr: measure.Def.Def, Overrides: NarrativesPayloadExpressionOverrides{ Classifications: []string{"measure"}, Format: measure.Def.NumFormat, }, Label: "", - } + }) } - for _, dimension := range obj.Properties().HyperCubeDef.Dimensions { - expressions = append(expressions, NarrativesPayloadExpression{ + dimensions := handler.Properties.HyperCubeDef.Dimensions + if handler.Properties.NlgChartObject != nil { + dimensions = handler.Properties.NlgChartObject.Dimensions + } + + for _, dimension := range dimensions { + if dimension.LibraryId != "" { + payload.LibItems = append(payload.LibItems, NarrativesPayloadLibItem{ + LibId: dimension.LibraryId, + Overrides: struct{}{}, + }) + continue + } + payload.Expressions = append(payload.Expressions, NarrativesPayloadExpression{ Expr: dimension.Def.FieldDefs[0], Overrides: NarrativesPayloadExpressionOverrides{ Classifications: []string{"dimension"}, @@ -108,16 +162,6 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State }) } - payload := NarrativesPayload{ - AppID: app.ID, - Lang: "en", - Verbosity: "full", - Expressions: expressions, - AnalysisTypes: nil, - Fields: nil, - LibItems: nil, - } - if obj.HyperCube().StateName != "" && obj.HyperCube().StateName != "$" { payload.AlternateStateName = obj.HyperCube().StateName } @@ -143,3 +187,27 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State sessionState.RegisterEvent(genObj.Handle, event, nil, true) } + +func (handler *NarrativesHandlerInstance) GetNarrativesProperties(sessionState *State, actionState *action.State, obj *enigmahandlers.Object) error { + enigmaObject, ok := obj.EnigmaObject.(*enigma.GenericObject) + if !ok { + return errors.Errorf("Failed to cast object<%s> to *enigma.GenericObject", obj.ID) + } + + //Get object properties + getProperties := func(ctx context.Context) error { + raw, err := enigmaObject.GetEffectivePropertiesRaw(ctx) + if err != nil { + return errors.Wrapf(err, "object<%s>.GetEffectiveProperties failed", obj.ID) + } + err = json.Unmarshal(raw, &handler.Properties) + if err != nil { + return errors.Wrapf(err, "object<%s>.GetEffectiveProperties unmarshal failed", obj.ID) + } + + obj.SetProperties(handler.Properties.GenericObjectProperties) + return nil + } + + return sessionState.SendRequest(actionState, getProperties) +} From 3c0dde71a7e7a2ab7ab62d5e66478e6d3faeeaaf Mon Sep 17 00:00:00 2001 From: Daniel Larsson Date: Mon, 3 Feb 2025 15:09:05 +0100 Subject: [PATCH 4/6] remove println --- session/narrativeshandler.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/session/narrativeshandler.go b/session/narrativeshandler.go index 46e4fa46..25ef77b4 100644 --- a/session/narrativeshandler.go +++ b/session/narrativeshandler.go @@ -80,8 +80,6 @@ func (handler *NarrativesHandlerInstance) GetObjectDefinition(objectType string) func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State, actionState *action.State, obj *enigmahandlers.Object, genObj *enigma.GenericObject) { var wg sync.WaitGroup - fmt.Println("handle object: ", obj.ID) - wg.Add(1) sessionState.QueueRequest(func(ctx context.Context) error { defer wg.Done() From e7f2b3926c81058fbb9b45871d180e03785375d8 Mon Sep 17 00:00:00 2001 From: Daniel Larsson Date: Mon, 3 Feb 2025 15:26:50 +0100 Subject: [PATCH 5/6] support QSEoW --- session/narrativeshandler.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/session/narrativeshandler.go b/session/narrativeshandler.go index 25ef77b4..be78f297 100644 --- a/session/narrativeshandler.go +++ b/session/narrativeshandler.go @@ -173,13 +173,24 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State protocol := sessionState.Rest.Protocol() host := sessionState.Rest.Host() - _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) + xrfKey := "" + xrfKeyState, exists := sessionState.GetCustomState(fmt.Sprintf("%s-%s", XrfKeyState, host)) + if exists { + xrfKey = fmt.Sprintf("?xrfkey=%s", xrfKeyState) + } + + _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate%s", protocol, host, xrfKey), actionState, sessionState.LogEntry, content, nil) event := func(ctx context.Context, as *action.State) error { if err := GetObjectLayout(sessionState, as, obj, nil); err != nil { return err } - _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate", protocol, host), actionState, sessionState.LogEntry, content, nil) + xrfKey := "" + xrfKeyState, exists := sessionState.GetCustomState(fmt.Sprintf("%s-%s", XrfKeyState, host)) + if exists { + xrfKey = fmt.Sprintf("?xrfkey=%s", xrfKeyState) + } + _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate%s", protocol, host, xrfKey), actionState, sessionState.LogEntry, content, nil) return nil } From 8f4b5f58fab466456f2317e27ea51998cb9d9043 Mon Sep 17 00:00:00 2001 From: Daniel Larsson Date: Tue, 4 Feb 2025 15:44:00 +0100 Subject: [PATCH 6/6] improve error handling --- session/narrativeshandler.go | 142 ++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 60 deletions(-) diff --git a/session/narrativeshandler.go b/session/narrativeshandler.go index be78f297..db7061b8 100644 --- a/session/narrativeshandler.go +++ b/session/narrativeshandler.go @@ -92,21 +92,83 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State return GetObjectLayout(sessionState, actionState, obj, nil) }, actionState, true, "") + wg.Wait() + if sessionState.Rest == nil { sessionState.LogEntry.Log(logger.WarningLevel, "no resthandler defined, nl insights object will not generated correctly") return } - wg.Wait() - app := sessionState.CurrentApp if app == nil || app.ID == "" { actionState.AddErrors(errors.Errorf("no current app found")) return } + content, err := handler.generateNarritivesPayload(app.ID, obj) + if err != nil { + actionState.AddErrors(err) + return + } + + protocol := sessionState.Rest.Protocol() + host := sessionState.Rest.Host() + + getXrfParam := func() string { + xrfKey := "" + xrfKeyState, exists := sessionState.GetCustomState(fmt.Sprintf("%s-%s", XrfKeyState, host)) + if exists { + xrfKey = fmt.Sprintf("?xrfkey=%s", xrfKeyState) + } + return xrfKey + } + + _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate%s", protocol, host, getXrfParam()), actionState, sessionState.LogEntry, content, nil) + + event := func(ctx context.Context, as *action.State) error { + if err := GetObjectLayout(sessionState, as, obj, nil); err != nil { + return err + } + + content, err := handler.generateNarritivesPayload(app.ID, obj) + if err != nil { + return err + } + + _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate%s", protocol, host, getXrfParam()), actionState, sessionState.LogEntry, content, nil) + return nil + } + + sessionState.RegisterEvent(genObj.Handle, event, nil, true) +} + +func (handler *NarrativesHandlerInstance) GetNarrativesProperties(sessionState *State, actionState *action.State, obj *enigmahandlers.Object) error { + enigmaObject, ok := obj.EnigmaObject.(*enigma.GenericObject) + if !ok { + return errors.Errorf("Failed to cast object<%s> to *enigma.GenericObject", obj.ID) + } + + //Get object properties + getProperties := func(ctx context.Context) error { + raw, err := enigmaObject.GetEffectivePropertiesRaw(ctx) + if err != nil { + return errors.Wrapf(err, "object<%s>.GetEffectiveProperties failed", obj.ID) + } + err = json.Unmarshal(raw, &handler.Properties) + if err != nil { + return errors.Wrapf(err, "object<%s>.GetEffectiveProperties unmarshal failed", obj.ID) + } + + obj.SetProperties(handler.Properties.GenericObjectProperties) + return nil + } + + return sessionState.SendRequest(actionState, getProperties) +} + +func (handler *NarrativesHandlerInstance) generateNarritivesPayload(appId string, obj *enigmahandlers.Object) ([]byte, error) { payload := NarrativesPayload{ - AppID: app.ID, + AppID: appId, Lang: "en", Verbosity: "full", Expressions: []NarrativesPayloadExpression{}, @@ -115,6 +177,16 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State LibItems: []NarrativesPayloadLibItem{}, } + if handler.Properties == nil { + return nil, errors.Errorf("no properties set for nl insights object<%s>", handler.ID) + } + + if handler.Properties.HyperCubeDef == nil { + return nil, errors.Errorf("no hypercube definition in properties for nl insights object<%s>", handler.ID) + } + + payload.AlternateStateName = handler.Properties.StateName + measures := handler.Properties.HyperCubeDef.Measures if handler.Properties.NlgChartObject != nil { measures = handler.Properties.NlgChartObject.Measures @@ -151,8 +223,12 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State }) continue } + expr := "" + if len(dimension.Def.FieldDefs) > 0 { + expr = dimension.Def.FieldDefs[0] + } payload.Expressions = append(payload.Expressions, NarrativesPayloadExpression{ - Expr: dimension.Def.FieldDefs[0], + Expr: expr, Overrides: NarrativesPayloadExpressionOverrides{ Classifications: []string{"dimension"}, }, @@ -160,63 +236,9 @@ func (handler *NarrativesHandlerInstance) SetObjectAndEvents(sessionState *State }) } - if obj.HyperCube().StateName != "" && obj.HyperCube().StateName != "$" { - payload.AlternateStateName = obj.HyperCube().StateName - } - content, err := json.Marshal(payload) if err != nil { - actionState.AddErrors(errors.Wrap(err, "failed to marshal narratives payload")) - return - } - - protocol := sessionState.Rest.Protocol() - host := sessionState.Rest.Host() - - xrfKey := "" - xrfKeyState, exists := sessionState.GetCustomState(fmt.Sprintf("%s-%s", XrfKeyState, host)) - if exists { - xrfKey = fmt.Sprintf("?xrfkey=%s", xrfKeyState) + return nil, errors.Wrap(err, "failed to marshal narratives payload") } - - _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate%s", protocol, host, xrfKey), actionState, sessionState.LogEntry, content, nil) - - event := func(ctx context.Context, as *action.State) error { - if err := GetObjectLayout(sessionState, as, obj, nil); err != nil { - return err - } - xrfKey := "" - xrfKeyState, exists := sessionState.GetCustomState(fmt.Sprintf("%s-%s", XrfKeyState, host)) - if exists { - xrfKey = fmt.Sprintf("?xrfkey=%s", xrfKeyState) - } - _, _ = sessionState.Rest.PostSync(fmt.Sprintf("%s%s/api/v1/narratives/actions/generate%s", protocol, host, xrfKey), actionState, sessionState.LogEntry, content, nil) - return nil - } - - sessionState.RegisterEvent(genObj.Handle, event, nil, true) -} - -func (handler *NarrativesHandlerInstance) GetNarrativesProperties(sessionState *State, actionState *action.State, obj *enigmahandlers.Object) error { - enigmaObject, ok := obj.EnigmaObject.(*enigma.GenericObject) - if !ok { - return errors.Errorf("Failed to cast object<%s> to *enigma.GenericObject", obj.ID) - } - - //Get object properties - getProperties := func(ctx context.Context) error { - raw, err := enigmaObject.GetEffectivePropertiesRaw(ctx) - if err != nil { - return errors.Wrapf(err, "object<%s>.GetEffectiveProperties failed", obj.ID) - } - err = json.Unmarshal(raw, &handler.Properties) - if err != nil { - return errors.Wrapf(err, "object<%s>.GetEffectiveProperties unmarshal failed", obj.ID) - } - - obj.SetProperties(handler.Properties.GenericObjectProperties) - return nil - } - - return sessionState.SendRequest(actionState, getProperties) + return content, nil }