diff --git a/data/local.yml b/data/local.yml deleted file mode 100644 index 4037359f4e..0000000000 --- a/data/local.yml +++ /dev/null @@ -1,15 +0,0 @@ -data_dir: "./data" -logger: - level: DEBUG -session: - token_expiry_sec: 86400 - refresh_token_expiry_sec: 86400 -socket: - max_message_size_bytes: 19420000 # ~20mb. Set to high values to enable the usage of the debug endpoint to populate the cache with a large number of profiles. - max_request_size_bytes: 19420000 -satori: - url: http://localhost:7450 - api_key_name: default - api_key: 29a4e29a-8942-40a0-9f54-7ee98ff27af1 - signing_key: defaultsigningkey - diff --git a/internal/satori/satori.go b/internal/satori/satori.go index 0b67c24315..50fdafe1cd 100644 --- a/internal/satori/satori.go +++ b/internal/satori/satori.go @@ -104,6 +104,11 @@ type authenticateBody struct { Id string `json:"id"` } +// @group satori +// @summary Create a new identity. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param id(type=string) The identifier of the identity. +// @return error(error) An optional error value if an error occurred. func (s *SatoriClient) Authenticate(ctx context.Context, id string) error { url := s.url.String() + "/v1/authenticate" @@ -134,7 +139,13 @@ func (s *SatoriClient) Authenticate(ctx context.Context, id string) error { } } -func (s *SatoriClient) PropertiesList(ctx context.Context, id string) (*runtime.Properties, error) { +// @group satori +// @summary Get identity properties. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @param id(type=string) The identifier of the identity. +// @return properties(type=*runtime.Properties) The identity properties. +// @return error(error) An optional error value if an error occurred. +func (s *SatoriClient) PropertiesGet(ctx context.Context, id string) (*runtime.Properties, error) { url := s.url.String() + "/v1/properties" sessionToken, err := s.generateToken(id) @@ -171,6 +182,12 @@ func (s *SatoriClient) PropertiesList(ctx context.Context, id string) (*runtime. } } +// @group satori +// @summary Update identity properties. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @oaram id(type=string) The identifier of the identity. +// @param properties(type=*runtime.PropertiesUpdate) The identity properties to update. +// @return error(error) An optional error value if an error occurred. func (s *SatoriClient) PropertiesUpdate(ctx context.Context, id string, properties *runtime.PropertiesUpdate) error { url := s.url.String() + "/v1/properties" @@ -217,6 +234,12 @@ func (e *event) setTimestamp() { e.TimestampPb = time.Unix(e.Timestamp, 0).Format(time.RFC3339) } +// @group satori +// @summary Publish an event. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @oaram id(type=string) The identifier of the identity. +// @param events(type=[]*runtime.Event) An array of events to publish. +// @return error(error) An optional error value if an error occurred. func (s *SatoriClient) EventsPublish(ctx context.Context, id string, events []*runtime.Event) error { url := s.url.String() + "/v1/event" @@ -258,6 +281,13 @@ func (s *SatoriClient) EventsPublish(ctx context.Context, id string, events []*r } } +// @group satori +// @summary List experiments. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=[]string, optional=true, default=[]) Optional list of experiment names to filter. +// @return experiments(type=*runtime.ExperimentList) The experiment list. +// @return error(error) An optional error value if an error occurred. func (s *SatoriClient) ExperimentsList(ctx context.Context, id string, names ...string) (*runtime.ExperimentList, error) { url := s.url.String() + "/v1/experiment" @@ -303,6 +333,13 @@ func (s *SatoriClient) ExperimentsList(ctx context.Context, id string, names ... } } +// @group satori +// @summary List flags. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=[]string, optional=true, default=[]) Optional list of flag names to filter. +// @return flags(type=*runtime.FlagList) The flag list. +// @return error(error) An optional error value if an error occurred. func (s *SatoriClient) FlagsList(ctx context.Context, id string, names ...string) (*runtime.FlagList, error) { url := s.url.String() + "/v1/flag" @@ -348,6 +385,13 @@ func (s *SatoriClient) FlagsList(ctx context.Context, id string, names ...string } } +// @group satori +// @summary List live events. +// @param ctx(type=context.Context) The context object represents information about the server and requester. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=[]string, optional=true, default=[]) Optional list of live event names to filter. +// @return liveEvents(type=*runtime.LiveEventsList) The live event list. +// @return error(error) An optional error value if an error occurred. func (s *SatoriClient) LiveEventsList(ctx context.Context, id string, names ...string) (*runtime.LiveEventList, error) { url := s.url.String() + "/v1/live-event" diff --git a/server/runtime_go_nakama.go b/server/runtime_go_nakama.go index 38da1a1354..b50d24b634 100644 --- a/server/runtime_go_nakama.go +++ b/server/runtime_go_nakama.go @@ -1442,7 +1442,7 @@ func (n *RuntimeGoNakamaModule) StreamSendRaw(mode uint8, subject, subcontext, l // @summary Disconnect a session. // @param ctx(type=context.Context) The context object represents information about the server and requester. // @param sessionId(type=string) The ID of the session to be disconnected. -// @param reason(type=runtime.PresenceReason) The reason for the session disconnect. +// @param reason(type=runtime.PresenceReason, optional=true) The reason for the session disconnect. // @return error(error) An optional error value if an error occurred. func (n *RuntimeGoNakamaModule) SessionDisconnect(ctx context.Context, sessionID string, reason ...runtime.PresenceReason) error { sid, err := uuid.FromString(sessionID) @@ -2782,7 +2782,7 @@ func (n *RuntimeGoNakamaModule) TournamentRecordsHaystack(ctx context.Context, i // @param userId(type=string) The user ID of the owner of the receipt. // @param receipt(type=string) Base-64 encoded receipt data returned by the purchase operation itself. // @param persist(type=bool) Persist the purchase so that seenBefore can be computed to protect against replay attacks. -// @param passwordOverride(type=string) Override the iap.apple.shared_password provided in your configuration. +// @param passwordOverride(type=string, optional=true) Override the iap.apple.shared_password provided in your configuration. // @return validation(*api.ValidatePurchaseResponse) The resulting successfully validated purchases. Any previously validated purchases are returned with a seenBefore flag. // @return error(error) An optional error value if an error occurred. func (n *RuntimeGoNakamaModule) PurchaseValidateApple(ctx context.Context, userID, receipt string, persist bool, passwordOverride ...string) (*api.ValidatePurchaseResponse, error) { @@ -2819,7 +2819,7 @@ func (n *RuntimeGoNakamaModule) PurchaseValidateApple(ctx context.Context, userI // @param userId(type=string) The user ID of the owner of the receipt. // @param receipt(type=string) JSON encoded Google receipt. // @param persist(type=bool) Persist the purchase so that seenBefore can be computed to protect against replay attacks. -// @param overrides(type=string) Override the iap.google.client_email and iap.google.private_key provided in your configuration. +// @param overrides(type=string, optional=true) Override the iap.google.client_email and iap.google.private_key provided in your configuration. // @return validation(*api.ValidatePurchaseResponse) The resulting successfully validated purchases. Any previously validated purchases are returned with a seenBefore flag. // @return error(error) An optional error value if an error occurred. func (n *RuntimeGoNakamaModule) PurchaseValidateGoogle(ctx context.Context, userID, receipt string, persist bool, overrides ...struct { @@ -2945,7 +2945,7 @@ func (n *RuntimeGoNakamaModule) PurchaseGetByTransactionId(ctx context.Context, // @param userId(type=string) The user ID of the owner of the receipt. // @param receipt(type=string) Base-64 encoded receipt data returned by the purchase operation itself. // @param persist(type=bool) Persist the subscription. -// @param passwordOverride(type=string) Override the iap.apple.shared_password provided in your configuration. +// @param passwordOverride(type=string, optional=true) Override the iap.apple.shared_password provided in your configuration. // @return validation(*api.ValidateSubscriptionResponse) The resulting successfully validated subscription purchase. // @return error(error) An optional error value if an error occurred. func (n *RuntimeGoNakamaModule) SubscriptionValidateApple(ctx context.Context, userID, receipt string, persist bool, passwordOverride ...string) (*api.ValidateSubscriptionResponse, error) { @@ -2982,7 +2982,7 @@ func (n *RuntimeGoNakamaModule) SubscriptionValidateApple(ctx context.Context, u // @param userId(type=string) The user ID of the owner of the receipt. // @param receipt(type=string) JSON encoded Google receipt. // @param persist(type=bool) Persist the subscription. -// @param overrides(type=string) Override the iap.google.client_email and iap.google.private_key provided in your configuration. +// @param overrides(type=string, optional=true) Override the iap.google.client_email and iap.google.private_key provided in your configuration. // @return validation(*api.ValidatePurchaseResponse) The resulting successfully validated subscription. // @return error(error) An optional error value if an error occurred. func (n *RuntimeGoNakamaModule) SubscriptionValidateGoogle(ctx context.Context, userID, receipt string, persist bool, overrides ...struct { @@ -3991,6 +3991,9 @@ func (n *RuntimeGoNakamaModule) ChannelIdBuild(ctx context.Context, senderId, ta return channelId, nil } +// @group satori +// @summary Get the Satori client. +// @return satori(runtime.Satori) The Satori client. func (n *RuntimeGoNakamaModule) GetSatori() runtime.Satori { return n.satori } diff --git a/server/runtime_javascript_nakama.go b/server/runtime_javascript_nakama.go index 1a0753e5a1..dc205882ed 100644 --- a/server/runtime_javascript_nakama.go +++ b/server/runtime_javascript_nakama.go @@ -116,260 +116,6 @@ func NewRuntimeJavascriptNakamaModule(logger *zap.Logger, db *sql.DB, protojsonM } } -func (n *runtimeJavascriptNakamaModule) satoriConstructor(r *goja.Runtime) (*goja.Object, error) { - constructor := func(call goja.ConstructorCall) *goja.Object { - call.This.Set("authenticate", func(f goja.FunctionCall) goja.Value { - id := getJsString(r, f.Argument(0)) - - if err := n.satori.Authenticate(n.ctx, id); err != nil { - n.logger.Error("Failed to Satori Authenticate.", zap.Error(err)) - panic(r.NewGoError(fmt.Errorf("failed to satori authenticate: %s", err.Error()))) - } - return nil - }) - - call.This.Set("propertiesList", func(f goja.FunctionCall) goja.Value { - id := getJsString(r, f.Argument(0)) - - props, err := n.satori.PropertiesList(n.ctx, id) - if err != nil { - n.logger.Error("Failed to Satori Authenticate.", zap.Error(err)) - panic(r.NewGoError(fmt.Errorf("failed to satori list properties: %s", err.Error()))) - } - - return r.ToValue(map[string]any{ - "default": props.Default, - "custom": props.Custom, - "computed": props.Computed, - }) - }) - - call.This.Set("propertiesUpdate", func(f goja.FunctionCall) goja.Value { - id := getJsString(r, f.Argument(0)) - - props, ok := f.Argument(1).Export().(map[string]any) - if !ok { - panic(r.NewTypeError("expects properties must be an object")) - } - - properties := &runtime.PropertiesUpdate{} - defProps, ok := props["default"] - if ok { - defPropsMap := getJsStringMap(r, r.ToValue(defProps)) - properties.Default = defPropsMap - } - customProps, ok := props["custom"] - if ok { - customPropsMap := getJsStringMap(r, r.ToValue(customProps)) - properties.Custom = customPropsMap - } - - if err := n.satori.PropertiesUpdate(n.ctx, id, properties); err != nil { - panic(r.NewGoError(fmt.Errorf("failed to satori update properties: %s", err.Error()))) - } - - return nil - }) - - call.This.Set("eventsPublish", func(f goja.FunctionCall) goja.Value { - identifier := getJsString(r, f.Argument(0)) - - events, ok := f.Argument(1).Export().([]any) - if !ok { - panic(r.NewTypeError("expects events must be an array")) - } - - evts := make([]*runtime.Event, 0, len(events)) - for _, e := range events { - eMap, ok := e.(map[string]any) - if !ok { - panic(r.NewTypeError("expects event to be an object")) - } - - evt := &runtime.Event{} - - name, ok := eMap["name"] - if ok { - nameStr, ok := name.(string) - if !ok { - panic(r.NewTypeError("expects event name to be a string")) - } - evt.Name = nameStr - } - - id, ok := eMap["id"] - if ok { - idStr, ok := id.(string) - if !ok { - panic(r.NewTypeError("expects event id to be a string")) - } - evt.Id = idStr - } - - metadata, ok := eMap["metadata"] - if ok { - metadataMap := getJsStringMap(r, r.ToValue(metadata)) - if !ok { - panic(r.NewTypeError("expects event metadata to be an object with string keys and values")) - } - evt.Metadata = metadataMap - } - - value, ok := eMap["value"] - if ok { - valueStr, ok := value.(string) - if !ok { - panic(r.NewTypeError("expects event value to be a string")) - } - evt.Value = valueStr - } - - ts, ok := eMap["timestamp"] - if ok { - tsInt, ok := ts.(int64) - if !ok { - panic(r.NewTypeError("expects event timestamp to be a number")) - } - evt.Timestamp = tsInt - } - - evts = append(evts, evt) - } - - if err := n.satori.EventsPublish(n.ctx, identifier, evts); err != nil { - panic(r.NewGoError(fmt.Errorf("failed to publish satori events: %s", err.Error()))) - } - - return nil - }) - - call.This.Set("experimentsList", func(f goja.FunctionCall) goja.Value { - identifier := getJsString(r, f.Argument(0)) - - nameFilters := make([]string, 0) - if !goja.IsUndefined(f.Argument(1)) && !goja.IsNull(f.Argument(1)) { - names := f.Argument(1) - - namesArray, ok := names.Export().([]any) - if ok { - for _, n := range namesArray { - ns, ok := n.(string) - if !ok { - panic(r.NewTypeError("expects name filter to be a string")) - } - nameFilters = append(nameFilters, ns) - } - } else { - panic(r.NewTypeError("expects names to be an array")) - } - } - - experimentList, err := n.satori.ExperimentsList(n.ctx, identifier, nameFilters...) - if err != nil { - panic(r.NewGoError(fmt.Errorf("failed to list satori experiments: %s", err.Error()))) - } - - experiments := make([]any, 0, len(experimentList.Experiments)) - for _, e := range experimentList.Experiments { - experiments = append(experiments, map[string]any{ - "name": e.Name, - "value": e.Value, - }) - } - - return r.ToValue(map[string]any{ - "experiments": experiments, - }) - }) - - call.This.Set("flagsList", func(f goja.FunctionCall) goja.Value { - identifier := getJsString(r, f.Argument(0)) - - nameFilters := make([]string, 0) - if !goja.IsUndefined(f.Argument(1)) && !goja.IsNull(f.Argument(1)) { - names := f.Argument(1) - - namesArray, ok := names.Export().([]any) - if ok { - for _, n := range namesArray { - ns, ok := n.(string) - if !ok { - panic(r.NewTypeError("expects name filter to be a string")) - } - nameFilters = append(nameFilters, ns) - } - } else { - panic(r.NewTypeError("expects names to be an array")) - } - } - - flagsList, err := n.satori.FlagsList(n.ctx, identifier, nameFilters...) - if err != nil { - panic(r.NewGoError(fmt.Errorf("failed to list satori flags: %s", err.Error()))) - } - - flags := make([]any, 0, len(flagsList.Flags)) - for _, flag := range flagsList.Flags { - flags = append(flags, map[string]any{ - "name": flag.Name, - "value": flag.Value, - "conditionChanged": flag.ConditionChanged, - }) - } - - return r.ToValue(map[string]any{ - "flags": flags, - }) - }) - - call.This.Set("liveEventsList", func(f goja.FunctionCall) goja.Value { - identifier := getJsString(r, f.Argument(0)) - - nameFilters := make([]string, 0) - if !goja.IsUndefined(f.Argument(1)) && !goja.IsNull(f.Argument(1)) { - names := f.Argument(1) - - namesArray, ok := names.Export().([]any) - if ok { - for _, n := range namesArray { - ns, ok := n.(string) - if !ok { - panic(r.NewTypeError("expects name filter to be a string")) - } - nameFilters = append(nameFilters, ns) - } - } else { - panic(r.NewTypeError("expects names to be an array")) - } - } - - liveEventsList, err := n.satori.LiveEventsList(n.ctx, identifier, nameFilters...) - if err != nil { - panic(r.NewGoError(fmt.Errorf("failed to list satori live-events %s:", err.Error()))) - } - - liveEvents := make([]any, 0, len(liveEventsList.LiveEvents)) - for _, le := range liveEventsList.LiveEvents { - liveEvents = append(liveEvents, map[string]any{ - "name": le.Name, - "description": le.Description, - "value": le.Value, - "activeStartTime": le.ActiveStartTimeSec, - "activeEndTime": le.ActiveEndTimeSec, - }) - } - - return r.ToValue(map[string]any{ - "liveEvents": liveEvents, - }) - }) - - return nil - } - - return r.New(r.ToValue(constructor)) -} - func (n *runtimeJavascriptNakamaModule) Constructor(r *goja.Runtime) (*goja.Object, error) { satoriJsObj, err := n.satoriConstructor(r) if err != nil { @@ -381,10 +127,7 @@ func (n *runtimeJavascriptNakamaModule) Constructor(r *goja.Runtime) (*goja.Obje call.This.Set(fnName, fn) } - // TODO: Add function docs - call.This.Set("getSatori", func(f goja.FunctionCall) goja.Value { - return satoriJsObj - }) + call.This.Set("getSatori", n.getSatori(satoriJsObj)) return nil } @@ -7959,7 +7702,7 @@ func (n *runtimeJavascriptNakamaModule) groupsList(r *goja.Runtime) func(goja.Fu // @group groups // @summary Fetch one or more groups randomly. // @param count(type=number) The number of groups to fetch. -// @return users(nkruntime.Group[]) A list of group record objects. +// @return groups(nkruntime.Group[]) A list of group record objects. // @return error(error) An optional error value if an error occurred. func (n *runtimeJavascriptNakamaModule) groupsGetRandom(r *goja.Runtime) func(goja.FunctionCall) goja.Value { return func(f goja.FunctionCall) goja.Value { @@ -8387,6 +8130,335 @@ func (n *runtimeJavascriptNakamaModule) channelIdBuild(r *goja.Runtime) func(goj } } +func (n *runtimeJavascriptNakamaModule) satoriConstructor(r *goja.Runtime) (*goja.Object, error) { + mappings := map[string]func(goja.FunctionCall) goja.Value{ + "authenticate": n.satoriAuthenticate(r), + "propertiesGet": n.satoriPropertiesGet(r), + "propertiesUpdate": n.satoriPropertiesUpdate(r), + "eventsPublish": n.satoriPublishEvents(r), + "experimentsList": n.satoriExperimentsList(r), + "flagsList": n.satoriFlagsList(r), + "liveEventsList": n.satoriLiveEventsList(r), + } + + constructor := func(call goja.ConstructorCall) *goja.Object { + for k, f := range mappings { + call.This.Set(k, f) + } + + return nil + } + + return r.New(r.ToValue(constructor)) +} + +// @group satori +// @summary Get the Satori client. +// @return satori(type=nkruntime.Satori) The satori client. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) getSatori(r *goja.Object) func(goja.FunctionCall) goja.Value { + return func(goja.FunctionCall) goja.Value { + return r + } +} + +// @group satori +// @summary Create a new identity. +// @param id(type=string) The identifier of the identity. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriAuthenticate(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + id := getJsString(r, f.Argument(0)) + + if err := n.satori.Authenticate(n.ctx, id); err != nil { + n.logger.Error("Failed to Satori Authenticate.", zap.Error(err)) + panic(r.NewGoError(fmt.Errorf("failed to satori authenticate: %s", err.Error()))) + } + return nil + } +} + +// @group satori +// @summary Get identity properties. +// @oaram id(type=string) The identifier of the identity. +// @return properties(type=nkruntime.Properties) The identity properties. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriPropertiesGet(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + id := getJsString(r, f.Argument(0)) + + props, err := n.satori.PropertiesGet(n.ctx, id) + if err != nil { + n.logger.Error("Failed to Satori Authenticate.", zap.Error(err)) + panic(r.NewGoError(fmt.Errorf("failed to satori list properties: %s", err.Error()))) + } + + return r.ToValue(map[string]any{ + "default": props.Default, + "custom": props.Custom, + "computed": props.Computed, + }) + } +} + +// @group satori +// @summary Update identity properties. +// @oaram id(type=string) The identifier of the identity. +// @param properties(type=nkruntime.PropertiesUpdate) The identity properties to update. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriPropertiesUpdate(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + id := getJsString(r, f.Argument(0)) + + props, ok := f.Argument(1).Export().(map[string]any) + if !ok { + panic(r.NewTypeError("expects properties must be an object")) + } + + properties := &runtime.PropertiesUpdate{} + defProps, ok := props["default"] + if ok { + defPropsMap := getJsStringMap(r, r.ToValue(defProps)) + properties.Default = defPropsMap + } + customProps, ok := props["custom"] + if ok { + customPropsMap := getJsStringMap(r, r.ToValue(customProps)) + properties.Custom = customPropsMap + } + + if err := n.satori.PropertiesUpdate(n.ctx, id, properties); err != nil { + panic(r.NewGoError(fmt.Errorf("failed to satori update properties: %s", err.Error()))) + } + + return nil + } +} + +// @group satori +// @summary Publish an event. +// @oaram id(type=string) The identifier of the identity. +// @param events(type=nkruntime.Event[]) An array of events to publish. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriPublishEvents(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + identifier := getJsString(r, f.Argument(0)) + + events, ok := f.Argument(1).Export().([]any) + if !ok { + panic(r.NewTypeError("expects events must be an array")) + } + + evts := make([]*runtime.Event, 0, len(events)) + for _, e := range events { + eMap, ok := e.(map[string]any) + if !ok { + panic(r.NewTypeError("expects event to be an object")) + } + + evt := &runtime.Event{} + + name, ok := eMap["name"] + if ok { + nameStr, ok := name.(string) + if !ok { + panic(r.NewTypeError("expects event name to be a string")) + } + evt.Name = nameStr + } + + id, ok := eMap["id"] + if ok { + idStr, ok := id.(string) + if !ok { + panic(r.NewTypeError("expects event id to be a string")) + } + evt.Id = idStr + } + + metadata, ok := eMap["metadata"] + if ok { + metadataMap := getJsStringMap(r, r.ToValue(metadata)) + if !ok { + panic(r.NewTypeError("expects event metadata to be an object with string keys and values")) + } + evt.Metadata = metadataMap + } + + value, ok := eMap["value"] + if ok { + valueStr, ok := value.(string) + if !ok { + panic(r.NewTypeError("expects event value to be a string")) + } + evt.Value = valueStr + } + + ts, ok := eMap["timestamp"] + if ok { + tsInt, ok := ts.(int64) + if !ok { + panic(r.NewTypeError("expects event timestamp to be a number")) + } + evt.Timestamp = tsInt + } + + evts = append(evts, evt) + } + + if err := n.satori.EventsPublish(n.ctx, identifier, evts); err != nil { + panic(r.NewGoError(fmt.Errorf("failed to publish satori events: %s", err.Error()))) + } + + return nil + } +} + +// @group satori +// @summary List experiments. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=string[], optional=true, default=[]) Optional list of experiment names to filter. +// @return experiments(type=nkruntime.Experiment[]) The experiment list. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriExperimentsList(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + identifier := getJsString(r, f.Argument(0)) + + nameFilters := make([]string, 0) + if !goja.IsUndefined(f.Argument(1)) && !goja.IsNull(f.Argument(1)) { + names := f.Argument(1) + + namesArray, ok := names.Export().([]any) + if ok { + for _, n := range namesArray { + ns, ok := n.(string) + if !ok { + panic(r.NewTypeError("expects name filter to be a string")) + } + nameFilters = append(nameFilters, ns) + } + } else { + panic(r.NewTypeError("expects names to be an array")) + } + } + + experimentList, err := n.satori.ExperimentsList(n.ctx, identifier, nameFilters...) + if err != nil { + panic(r.NewGoError(fmt.Errorf("failed to list satori experiments: %s", err.Error()))) + } + + experiments := make([]any, 0, len(experimentList.Experiments)) + for _, e := range experimentList.Experiments { + experiments = append(experiments, map[string]any{ + "name": e.Name, + "value": e.Value, + }) + } + + return r.ToValue(map[string]any{ + "experiments": experiments, + }) + } +} + +// @group satori +// @summary List flags. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=string[], optional=true, default=[]) Optional list of flag names to filter. +// @return flags(type=nkruntime.Flag[]) The flag list. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriFlagsList(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + identifier := getJsString(r, f.Argument(0)) + + nameFilters := make([]string, 0) + if !goja.IsUndefined(f.Argument(1)) && !goja.IsNull(f.Argument(1)) { + names := f.Argument(1) + + namesArray, ok := names.Export().([]any) + if ok { + for _, n := range namesArray { + ns, ok := n.(string) + if !ok { + panic(r.NewTypeError("expects name filter to be a string")) + } + nameFilters = append(nameFilters, ns) + } + } else { + panic(r.NewTypeError("expects names to be an array")) + } + } + + flagsList, err := n.satori.FlagsList(n.ctx, identifier, nameFilters...) + if err != nil { + panic(r.NewGoError(fmt.Errorf("failed to list satori flags: %s", err.Error()))) + } + + flags := make([]any, 0, len(flagsList.Flags)) + for _, flag := range flagsList.Flags { + flags = append(flags, map[string]any{ + "name": flag.Name, + "value": flag.Value, + "conditionChanged": flag.ConditionChanged, + }) + } + + return r.ToValue(map[string]any{ + "flags": flags, + }) + } +} + +// @group satori +// @summary List live events. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=string[], optional=true, default=[]) Optional list of live event names to filter. +// @return liveEvents(type=nkruntime.LiveEvent[]) The live event list. +// @return error(error) An optional error value if an error occurred. +func (n *runtimeJavascriptNakamaModule) satoriLiveEventsList(r *goja.Runtime) func(goja.FunctionCall) goja.Value { + return func(f goja.FunctionCall) goja.Value { + identifier := getJsString(r, f.Argument(0)) + + nameFilters := make([]string, 0) + if !goja.IsUndefined(f.Argument(1)) && !goja.IsNull(f.Argument(1)) { + names := f.Argument(1) + + namesArray, ok := names.Export().([]any) + if ok { + for _, n := range namesArray { + ns, ok := n.(string) + if !ok { + panic(r.NewTypeError("expects name filter to be a string")) + } + nameFilters = append(nameFilters, ns) + } + } else { + panic(r.NewTypeError("expects names to be an array")) + } + } + + liveEventsList, err := n.satori.LiveEventsList(n.ctx, identifier, nameFilters...) + if err != nil { + panic(r.NewGoError(fmt.Errorf("failed to list satori live-events %s:", err.Error()))) + } + + liveEvents := make([]any, 0, len(liveEventsList.LiveEvents)) + for _, le := range liveEventsList.LiveEvents { + liveEvents = append(liveEvents, map[string]any{ + "name": le.Name, + "description": le.Description, + "value": le.Value, + "activeStartTime": le.ActiveStartTimeSec, + "activeEndTime": le.ActiveEndTimeSec, + }) + } + + return r.ToValue(map[string]any{ + "liveEvents": liveEvents, + }) + } +} + func getJsString(r *goja.Runtime, v goja.Value) string { s, ok := v.Export().(string) if !ok { diff --git a/server/runtime_lua_nakama.go b/server/runtime_lua_nakama.go index d554b93c5b..47c1401b70 100644 --- a/server/runtime_lua_nakama.go +++ b/server/runtime_lua_nakama.go @@ -9733,16 +9733,19 @@ func (n *RuntimeLuaNakamaModule) channelIdBuild(l *lua.LState) int { return 1 } -// TODO: Add doc tags +// @group satori +// @summary Get the Satori client. +// @return satori(table) The satori client. +// @return error(error) An optional error value if an error occurred. func (n *RuntimeLuaNakamaModule) getSatori(l *lua.LState) int { satoriFunctions := map[string]lua.LGFunction{ "authenticate": n.satoriAuthenticate, - "properties_list": n.satoriListProperties, - "properties_update": n.satoriUpdateProperties, - "events_publish": n.satoriPublishEvent, - "experiments_list": n.satoriListExperiments, - "flags_list": n.satoriListFlags, - "live_events_list": n.satoriListLiveEvents, + "properties_get": n.satoriPropertiesGet, + "properties_update": n.satoriPropertiesUpdate, + "events_publish": n.satoriEventsPublish, + "experiments_list": n.satoriExperimentsList, + "flags_list": n.satoriFlagsList, + "live_events_list": n.satoriLiveEventsList, } satoriMod := l.SetFuncs(l.CreateTable(0, len(satoriFunctions)), satoriFunctions) @@ -9751,6 +9754,10 @@ func (n *RuntimeLuaNakamaModule) getSatori(l *lua.LState) int { return 1 } +// @group satori +// @summary Create a new identity. +// @param id(type=string) The identifier of the identity. +// @return error(error) An optional error value if an error occurred. func (n *RuntimeLuaNakamaModule) satoriAuthenticate(l *lua.LState) int { identifier := l.CheckString(1) @@ -9762,10 +9769,15 @@ func (n *RuntimeLuaNakamaModule) satoriAuthenticate(l *lua.LState) int { return 0 } -func (n *RuntimeLuaNakamaModule) satoriListProperties(l *lua.LState) int { +// @group satori +// @summary Get identity properties. +// @oaram id(type=string) The identifier of the identity. +// @return properties(type=table) The identity properties. +// @return error(error) An optional error value if an error occurred. +func (n *RuntimeLuaNakamaModule) satoriPropertiesGet(l *lua.LState) int { identifier := l.CheckString(1) - props, err := n.satori.PropertiesList(l.Context(), identifier) + props, err := n.satori.PropertiesGet(l.Context(), identifier) if err != nil { l.RaiseError("failed to satori list properties: %v", err.Error()) return 0 @@ -9780,7 +9792,12 @@ func (n *RuntimeLuaNakamaModule) satoriListProperties(l *lua.LState) int { return 1 } -func (n *RuntimeLuaNakamaModule) satoriUpdateProperties(l *lua.LState) int { +// @group satori +// @summary Update identity properties. +// @oaram id(type=string) The identifier of the identity. +// @param properties(type=table) The identity properties to update. +// @return error(error) An optional error value if an error occurred. +func (n *RuntimeLuaNakamaModule) satoriPropertiesUpdate(l *lua.LState) int { identifier := l.CheckString(1) propertiesTable := l.CheckTable(2) @@ -9836,7 +9853,12 @@ func (n *RuntimeLuaNakamaModule) satoriUpdateProperties(l *lua.LState) int { return 0 } -func (n *RuntimeLuaNakamaModule) satoriPublishEvent(l *lua.LState) int { +// @group satori +// @summary Publish an event. +// @oaram id(type=string) The identifier of the identity. +// @param events(type=table) An array of events to publish. +// @return error(error) An optional error value if an error occurred. +func (n *RuntimeLuaNakamaModule) satoriEventsPublish(l *lua.LState) int { identifier := l.CheckString(1) eventsTable := l.CheckTable(2) @@ -9928,7 +9950,13 @@ func (n *RuntimeLuaNakamaModule) satoriPublishEvent(l *lua.LState) int { return 0 } -func (n *RuntimeLuaNakamaModule) satoriListExperiments(l *lua.LState) int { +// @group satori +// @summary List experiments. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=table, optional=true, default=[]) Optional list of experiment names to filter. +// @return experiments(table) The experiment list. +// @return error(error) An optional error value if an error occurred. +func (n *RuntimeLuaNakamaModule) satoriExperimentsList(l *lua.LState) int { identifier := l.CheckString(1) namesTable := l.OptTable(2, nil) @@ -9972,7 +10000,13 @@ func (n *RuntimeLuaNakamaModule) satoriListExperiments(l *lua.LState) int { return 1 } -func (n *RuntimeLuaNakamaModule) satoriListFlags(l *lua.LState) int { +// @group satori +// @summary List flags. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=table, optional=true, default=[]) Optional list of flag names to filter. +// @return flags(table) The flag list. +// @return error(error) An optional error value if an error occurred. +func (n *RuntimeLuaNakamaModule) satoriFlagsList(l *lua.LState) int { identifier := l.CheckString(1) namesTable := l.OptTable(2, nil) @@ -10017,7 +10051,13 @@ func (n *RuntimeLuaNakamaModule) satoriListFlags(l *lua.LState) int { return 1 } -func (n *RuntimeLuaNakamaModule) satoriListLiveEvents(l *lua.LState) int { +// @group satori +// @summary List live events. +// @oaram id(type=string) The identifier of the identity. +// @param names(type=table, optional=true, default=[]) Optional list of live event names to filter. +// @return liveEvents(table) The live event list. +// @return error(error) An optional error value if an error occurred. +func (n *RuntimeLuaNakamaModule) satoriLiveEventsList(l *lua.LState) int { identifier := l.CheckString(1) namesTable := l.OptTable(2, nil) diff --git a/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go b/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go index 99e166fe62..646cba7633 100644 --- a/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go +++ b/vendor/github.com/heroiclabs/nakama-common/runtime/runtime.go @@ -1136,11 +1136,12 @@ type NakamaModule interface { GetSatori() Satori } -// Satori - +/* +Satori runtime integration defintions. +*/ type Satori interface { Authenticate(ctx context.Context, id string) error - PropertiesList(ctx context.Context, id string) (*Properties, error) + PropertiesGet(ctx context.Context, id string) (*Properties, error) PropertiesUpdate(ctx context.Context, id string, properties *PropertiesUpdate) error EventsPublish(ctx context.Context, id string, events []*Event) error ExperimentsList(ctx context.Context, id string, names ...string) (*ExperimentList, error)