Attempt to using Nakama as replacement of Low-Level ENet
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
extends SatoriAsyncResult
|
||||
|
||||
class_name Event
|
||||
|
||||
# The name of the event.
|
||||
var name: String
|
||||
|
||||
# The time when the event was triggered.
|
||||
var timestamp: String
|
||||
|
||||
# Optional value.
|
||||
var value: String
|
||||
|
||||
# Event metadata, if any.
|
||||
var metadata: Dictionary
|
||||
|
||||
# Optional event ID assigned by the client, used to de-duplicate in retransmission scenarios.
|
||||
# If not supplied the server will assign a randomly generated unique event identifier.
|
||||
var id: String
|
||||
|
||||
# The event constructor.
|
||||
# Initializes a new Event object.
|
||||
#
|
||||
# @param name The name of the event.
|
||||
# @param timestamp The timestamp of the event.
|
||||
# @param value The value associated with the event (optional).
|
||||
# @param metadata The metadata associated with the event (optional).
|
||||
# @param id The ID of the event (optional).
|
||||
func _init(name: String, timestamp: float, value: String = "", metadata: Dictionary = {}, id: String = "", p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
self.name = name
|
||||
self.timestamp = unix_to_protobuf_timestamp_format(timestamp)
|
||||
self.value = value
|
||||
self.metadata = metadata
|
||||
self.id = id
|
||||
|
||||
func to_api_event_dict() -> Dictionary:
|
||||
return {
|
||||
"name": self.name,
|
||||
"timestamp": self.timestamp,
|
||||
"value": self.value,
|
||||
"metadata": self.metadata,
|
||||
"id": self.id
|
||||
}
|
||||
|
||||
func unix_to_protobuf_timestamp_format(unix_time: float) -> String:
|
||||
# Extract microseconds precision from unix time
|
||||
var microseconds = int(fmod(unix_time, 1.0) * 1_000_000)
|
||||
|
||||
# Convert seconds to datetime structure
|
||||
var datetime = Time.get_datetime_dict_from_unix_time(int(unix_time))
|
||||
|
||||
var year = datetime.year
|
||||
var month = str(datetime.month).pad_zeros(2)
|
||||
var day = str(datetime.day).pad_zeros(2)
|
||||
var hour = str(datetime.hour).pad_zeros(2)
|
||||
var minute = str(datetime.minute).pad_zeros(2)
|
||||
var second = str(datetime.second).pad_zeros(2)
|
||||
var microsecond = str(microseconds).pad_zeros(6)
|
||||
|
||||
# Construct the protobuf timestamp format string
|
||||
var timestamp_str = "%s-%s-%sT%s:%s:%s.%sZ" % [year, month, day, hour, minute, second, microsecond]
|
||||
|
||||
return timestamp_str
|
||||
@@ -0,0 +1 @@
|
||||
uid://ccldwb3ljqre2
|
||||
@@ -0,0 +1,1396 @@
|
||||
### Code generated by codegen/main.go. DO NOT EDIT. ###
|
||||
|
||||
extends RefCounted
|
||||
class_name SatoriAPI
|
||||
|
||||
# Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user.
|
||||
class ApiAuthenticateLogoutRequest extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false},
|
||||
"token": {"name": "_token", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Refresh token to invalidate.
|
||||
var _refresh_token
|
||||
var refresh_token : String:
|
||||
get:
|
||||
return "" if not _refresh_token is String else String(_refresh_token)
|
||||
|
||||
# Session token to log out.
|
||||
var _token
|
||||
var token : String:
|
||||
get:
|
||||
return "" if not _token is String else String(_token)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAuthenticateLogoutRequest:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiAuthenticateLogoutRequest", p_dict), ApiAuthenticateLogoutRequest) as ApiAuthenticateLogoutRequest
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "refresh_token: %s, " % _refresh_token
|
||||
output += "token: %s, " % _token
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# Authenticate against the server with a refresh token.
|
||||
class ApiAuthenticateRefreshRequest extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Refresh token.
|
||||
var _refresh_token
|
||||
var refresh_token : String:
|
||||
get:
|
||||
return "" if not _refresh_token is String else String(_refresh_token)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAuthenticateRefreshRequest:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiAuthenticateRefreshRequest", p_dict), ApiAuthenticateRefreshRequest) as ApiAuthenticateRefreshRequest
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "refresh_token: %s, " % _refresh_token
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
#
|
||||
class ApiAuthenticateRequest extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"id": {"name": "_id", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Optional custom properties to update with this call.
|
||||
# If not set, properties are left as they are on the server.
|
||||
var _custom
|
||||
var custom : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _custom is Dictionary else _custom.duplicate()
|
||||
|
||||
# Optional default properties to update with this call.
|
||||
# If not set, properties are left as they are on the server.
|
||||
var _default
|
||||
var default : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _default is Dictionary else _default.duplicate()
|
||||
|
||||
# Identity ID. Must be between eight and 128 characters (inclusive).
|
||||
# Must be an alphanumeric string with only underscores and hyphens allowed.
|
||||
var _id
|
||||
var id : String:
|
||||
get:
|
||||
return "" if not _id is String else String(_id)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAuthenticateRequest:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiAuthenticateRequest", p_dict), ApiAuthenticateRequest) as ApiAuthenticateRequest
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
if typeof(_custom) == TYPE_DICTIONARY:
|
||||
for k in _custom:
|
||||
map_string += "{%s=%s}, " % [k, _custom[k]]
|
||||
output += "custom: [%s], " % map_string
|
||||
map_string = ""
|
||||
if typeof(_default) == TYPE_DICTIONARY:
|
||||
for k in _default:
|
||||
map_string += "{%s=%s}, " % [k, _default[k]]
|
||||
output += "default: [%s], " % map_string
|
||||
map_string = ""
|
||||
output += "id: %s, " % _id
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# A single event. Usually, but not necessarily, part of a batch.
|
||||
class ApiEvent extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"id": {"name": "_id", "type": TYPE_STRING, "required": false},
|
||||
"metadata": {"name": "_metadata", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"name": {"name": "_name", "type": TYPE_STRING, "required": false},
|
||||
"timestamp": {"name": "_timestamp", "type": TYPE_STRING, "required": false},
|
||||
"value": {"name": "_value", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Optional event ID assigned by the client, used to de-duplicate in retransmission scenarios.
|
||||
# If not supplied the server will assign a randomly generated unique event identifier.
|
||||
var _id
|
||||
var id : String:
|
||||
get:
|
||||
return "" if not _id is String else String(_id)
|
||||
|
||||
# Event metadata, if any.
|
||||
var _metadata
|
||||
var metadata : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _metadata is Dictionary else _metadata.duplicate()
|
||||
|
||||
# Event name.
|
||||
var _name
|
||||
var name : String:
|
||||
get:
|
||||
return "" if not _name is String else String(_name)
|
||||
|
||||
# The time when the event was triggered on the producer side.
|
||||
var _timestamp
|
||||
var timestamp : String:
|
||||
get:
|
||||
return "" if not _timestamp is String else String(_timestamp)
|
||||
|
||||
# Optional value.
|
||||
var _value
|
||||
var value : String:
|
||||
get:
|
||||
return "" if not _value is String else String(_value)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiEvent:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiEvent", p_dict), ApiEvent) as ApiEvent
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "id: %s, " % _id
|
||||
if typeof(_metadata) == TYPE_DICTIONARY:
|
||||
for k in _metadata:
|
||||
map_string += "{%s=%s}, " % [k, _metadata[k]]
|
||||
output += "metadata: [%s], " % map_string
|
||||
map_string = ""
|
||||
output += "name: %s, " % _name
|
||||
output += "timestamp: %s, " % _timestamp
|
||||
output += "value: %s, " % _value
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
#
|
||||
class ApiEventRequest extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"events": {"name": "_events", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY},
|
||||
}
|
||||
|
||||
# Some number of events produced by a client.
|
||||
var _events
|
||||
var events : Array:
|
||||
get:
|
||||
return Array() if not _events is Array else Array(_events)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiEventRequest:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiEventRequest", p_dict), ApiEventRequest) as ApiEventRequest
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "events: %s, " % [_events]
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# An experiment that this user is partaking.
|
||||
class ApiExperiment extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"name": {"name": "_name", "type": TYPE_STRING, "required": false},
|
||||
"value": {"name": "_value", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
#
|
||||
var _name
|
||||
var name : String:
|
||||
get:
|
||||
return "" if not _name is String else String(_name)
|
||||
|
||||
# Value associated with this Experiment.
|
||||
var _value
|
||||
var value : String:
|
||||
get:
|
||||
return "" if not _value is String else String(_value)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiExperiment:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiExperiment", p_dict), ApiExperiment) as ApiExperiment
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "name: %s, " % _name
|
||||
output += "value: %s, " % _value
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# All experiments that this identity is involved with.
|
||||
class ApiExperimentList extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"experiments": {"name": "_experiments", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY},
|
||||
}
|
||||
|
||||
# All experiments for this identity.
|
||||
var _experiments
|
||||
var experiments : Array:
|
||||
get:
|
||||
return Array() if not _experiments is Array else Array(_experiments)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiExperimentList:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiExperimentList", p_dict), ApiExperimentList) as ApiExperimentList
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "experiments: %s, " % [_experiments]
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# Feature flag available to the identity.
|
||||
class ApiFlag extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"condition_changed": {"name": "_condition_changed", "type": TYPE_BOOL, "required": false},
|
||||
"name": {"name": "_name", "type": TYPE_STRING, "required": false},
|
||||
"value": {"name": "_value", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Whether the value for this flag has conditionally changed from the default state.
|
||||
var _condition_changed
|
||||
var condition_changed : bool:
|
||||
get:
|
||||
return false if not _condition_changed is bool else bool(_condition_changed)
|
||||
|
||||
#
|
||||
var _name
|
||||
var name : String:
|
||||
get:
|
||||
return "" if not _name is String else String(_name)
|
||||
|
||||
# Value associated with this flag.
|
||||
var _value
|
||||
var value : String:
|
||||
get:
|
||||
return "" if not _value is String else String(_value)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFlag:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiFlag", p_dict), ApiFlag) as ApiFlag
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "condition_changed: %s, " % _condition_changed
|
||||
output += "name: %s, " % _name
|
||||
output += "value: %s, " % _value
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
#
|
||||
class ApiFlagList extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"flags": {"name": "_flags", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY},
|
||||
}
|
||||
|
||||
#
|
||||
var _flags
|
||||
var flags : Array:
|
||||
get:
|
||||
return Array() if not _flags is Array else Array(_flags)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFlagList:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiFlagList", p_dict), ApiFlagList) as ApiFlagList
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "flags: %s, " % [_flags]
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# A response containing all the messages for an identity.
|
||||
class ApiGetMessageListResponse extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"cacheable_cursor": {"name": "_cacheable_cursor", "type": TYPE_STRING, "required": false},
|
||||
"messages": {"name": "_messages", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY},
|
||||
"next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false},
|
||||
"prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Cacheable cursor to list newer messages. Durable and designed to be stored, unlike next/prev cursors.
|
||||
var _cacheable_cursor
|
||||
var cacheable_cursor : String:
|
||||
get:
|
||||
return "" if not _cacheable_cursor is String else String(_cacheable_cursor)
|
||||
|
||||
# The list of messages.
|
||||
var _messages
|
||||
var messages : Array:
|
||||
get:
|
||||
return Array() if not _messages is Array else Array(_messages)
|
||||
|
||||
# The cursor to send when retrieving the next page, if any.
|
||||
var _next_cursor
|
||||
var next_cursor : String:
|
||||
get:
|
||||
return "" if not _next_cursor is String else String(_next_cursor)
|
||||
|
||||
# The cursor to send when retrieving the previous page, if any.
|
||||
var _prev_cursor
|
||||
var prev_cursor : String:
|
||||
get:
|
||||
return "" if not _prev_cursor is String else String(_prev_cursor)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGetMessageListResponse:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiGetMessageListResponse", p_dict), ApiGetMessageListResponse) as ApiGetMessageListResponse
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "cacheable_cursor: %s, " % _cacheable_cursor
|
||||
output += "messages: %s, " % [_messages]
|
||||
output += "next_cursor: %s, " % _next_cursor
|
||||
output += "prev_cursor: %s, " % _prev_cursor
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# Enrich/replace the current session with a new ID.
|
||||
class ApiIdentifyRequest extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"id": {"name": "_id", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Optional custom properties to update with this call.
|
||||
# If not set, properties are left as they are on the server.
|
||||
var _custom
|
||||
var custom : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _custom is Dictionary else _custom.duplicate()
|
||||
|
||||
# Optional default properties to update with this call.
|
||||
# If not set, properties are left as they are on the server.
|
||||
var _default
|
||||
var default : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _default is Dictionary else _default.duplicate()
|
||||
|
||||
# Identity ID to enrich the current session and return a new session. Old session will no longer be usable.
|
||||
var _id
|
||||
var id : String:
|
||||
get:
|
||||
return "" if not _id is String else String(_id)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiIdentifyRequest:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiIdentifyRequest", p_dict), ApiIdentifyRequest) as ApiIdentifyRequest
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
if typeof(_custom) == TYPE_DICTIONARY:
|
||||
for k in _custom:
|
||||
map_string += "{%s=%s}, " % [k, _custom[k]]
|
||||
output += "custom: [%s], " % map_string
|
||||
map_string = ""
|
||||
if typeof(_default) == TYPE_DICTIONARY:
|
||||
for k in _default:
|
||||
map_string += "{%s=%s}, " % [k, _default[k]]
|
||||
output += "default: [%s], " % map_string
|
||||
map_string = ""
|
||||
output += "id: %s, " % _id
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# A single live event.
|
||||
class ApiLiveEvent extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"active_end_time_sec": {"name": "_active_end_time_sec", "type": TYPE_STRING, "required": false},
|
||||
"active_start_time_sec": {"name": "_active_start_time_sec", "type": TYPE_STRING, "required": false},
|
||||
"description": {"name": "_description", "type": TYPE_STRING, "required": false},
|
||||
"id": {"name": "_id", "type": TYPE_STRING, "required": false},
|
||||
"name": {"name": "_name", "type": TYPE_STRING, "required": false},
|
||||
"value": {"name": "_value", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# End time of current event run.
|
||||
var _active_end_time_sec
|
||||
var active_end_time_sec : String:
|
||||
get:
|
||||
return "" if not _active_end_time_sec is String else String(_active_end_time_sec)
|
||||
|
||||
# Start time of current event run.
|
||||
var _active_start_time_sec
|
||||
var active_start_time_sec : String:
|
||||
get:
|
||||
return "" if not _active_start_time_sec is String else String(_active_start_time_sec)
|
||||
|
||||
# Description.
|
||||
var _description
|
||||
var description : String:
|
||||
get:
|
||||
return "" if not _description is String else String(_description)
|
||||
|
||||
# The live event identifier.
|
||||
var _id
|
||||
var id : String:
|
||||
get:
|
||||
return "" if not _id is String else String(_id)
|
||||
|
||||
# Name.
|
||||
var _name
|
||||
var name : String:
|
||||
get:
|
||||
return "" if not _name is String else String(_name)
|
||||
|
||||
# Event value.
|
||||
var _value
|
||||
var value : String:
|
||||
get:
|
||||
return "" if not _value is String else String(_value)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLiveEvent:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiLiveEvent", p_dict), ApiLiveEvent) as ApiLiveEvent
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "active_end_time_sec: %s, " % _active_end_time_sec
|
||||
output += "active_start_time_sec: %s, " % _active_start_time_sec
|
||||
output += "description: %s, " % _description
|
||||
output += "id: %s, " % _id
|
||||
output += "name: %s, " % _name
|
||||
output += "value: %s, " % _value
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# List of Live events.
|
||||
class ApiLiveEventList extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"live_events": {"name": "_live_events", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY},
|
||||
}
|
||||
|
||||
# Live events.
|
||||
var _live_events
|
||||
var live_events : Array:
|
||||
get:
|
||||
return Array() if not _live_events is Array else Array(_live_events)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLiveEventList:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiLiveEventList", p_dict), ApiLiveEventList) as ApiLiveEventList
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "live_events: %s, " % [_live_events]
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# A scheduled message.
|
||||
class ApiMessage extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"consume_time": {"name": "_consume_time", "type": TYPE_STRING, "required": false},
|
||||
"create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
|
||||
"metadata": {"name": "_metadata", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"read_time": {"name": "_read_time", "type": TYPE_STRING, "required": false},
|
||||
"schedule_id": {"name": "_schedule_id", "type": TYPE_STRING, "required": false},
|
||||
"send_time": {"name": "_send_time", "type": TYPE_STRING, "required": false},
|
||||
"text": {"name": "_text", "type": TYPE_STRING, "required": false},
|
||||
"update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# The time the message was consumed by the identity.
|
||||
var _consume_time
|
||||
var consume_time : String:
|
||||
get:
|
||||
return "" if not _consume_time is String else String(_consume_time)
|
||||
|
||||
# The time the message was created.
|
||||
var _create_time
|
||||
var create_time : String:
|
||||
get:
|
||||
return "" if not _create_time is String else String(_create_time)
|
||||
|
||||
# A key-value pairs of metadata.
|
||||
var _metadata
|
||||
var metadata : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _metadata is Dictionary else _metadata.duplicate()
|
||||
|
||||
# The time the message was read by the client.
|
||||
var _read_time
|
||||
var read_time : String:
|
||||
get:
|
||||
return "" if not _read_time is String else String(_read_time)
|
||||
|
||||
# The identifier of the schedule.
|
||||
var _schedule_id
|
||||
var schedule_id : String:
|
||||
get:
|
||||
return "" if not _schedule_id is String else String(_schedule_id)
|
||||
|
||||
# The send time for the message.
|
||||
var _send_time
|
||||
var send_time : String:
|
||||
get:
|
||||
return "" if not _send_time is String else String(_send_time)
|
||||
|
||||
# The message's text.
|
||||
var _text
|
||||
var text : String:
|
||||
get:
|
||||
return "" if not _text is String else String(_text)
|
||||
|
||||
# The time the message was updated.
|
||||
var _update_time
|
||||
var update_time : String:
|
||||
get:
|
||||
return "" if not _update_time is String else String(_update_time)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiMessage:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiMessage", p_dict), ApiMessage) as ApiMessage
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "consume_time: %s, " % _consume_time
|
||||
output += "create_time: %s, " % _create_time
|
||||
if typeof(_metadata) == TYPE_DICTIONARY:
|
||||
for k in _metadata:
|
||||
map_string += "{%s=%s}, " % [k, _metadata[k]]
|
||||
output += "metadata: [%s], " % map_string
|
||||
map_string = ""
|
||||
output += "read_time: %s, " % _read_time
|
||||
output += "schedule_id: %s, " % _schedule_id
|
||||
output += "send_time: %s, " % _send_time
|
||||
output += "text: %s, " % _text
|
||||
output += "update_time: %s, " % _update_time
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# Properties associated with an identity.
|
||||
class ApiProperties extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"computed": {"name": "_computed", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
}
|
||||
|
||||
# Event computed properties.
|
||||
var _computed
|
||||
var computed : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _computed is Dictionary else _computed.duplicate()
|
||||
|
||||
# Event custom properties.
|
||||
var _custom
|
||||
var custom : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _custom is Dictionary else _custom.duplicate()
|
||||
|
||||
# Event default properties.
|
||||
var _default
|
||||
var default : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _default is Dictionary else _default.duplicate()
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiProperties:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiProperties", p_dict), ApiProperties) as ApiProperties
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
if typeof(_computed) == TYPE_DICTIONARY:
|
||||
for k in _computed:
|
||||
map_string += "{%s=%s}, " % [k, _computed[k]]
|
||||
output += "computed: [%s], " % map_string
|
||||
map_string = ""
|
||||
if typeof(_custom) == TYPE_DICTIONARY:
|
||||
for k in _custom:
|
||||
map_string += "{%s=%s}, " % [k, _custom[k]]
|
||||
output += "custom: [%s], " % map_string
|
||||
map_string = ""
|
||||
if typeof(_default) == TYPE_DICTIONARY:
|
||||
for k in _default:
|
||||
map_string += "{%s=%s}, " % [k, _default[k]]
|
||||
output += "default: [%s], " % map_string
|
||||
map_string = ""
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# A session.
|
||||
class ApiSession extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"properties": {"name": "_properties", "type": "ApiProperties", "required": false},
|
||||
"refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false},
|
||||
"token": {"name": "_token", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# Properties associated with this identity.
|
||||
var _properties
|
||||
var properties : ApiProperties:
|
||||
get:
|
||||
return _properties as ApiProperties
|
||||
|
||||
# Refresh token.
|
||||
var _refresh_token
|
||||
var refresh_token : String:
|
||||
get:
|
||||
return "" if not _refresh_token is String else String(_refresh_token)
|
||||
|
||||
# Token credential.
|
||||
var _token
|
||||
var token : String:
|
||||
get:
|
||||
return "" if not _token is String else String(_token)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSession:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiSession", p_dict), ApiSession) as ApiSession
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "properties: %s, " % _properties
|
||||
output += "refresh_token: %s, " % _refresh_token
|
||||
output += "token: %s, " % _token
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# Update Properties associated with this identity.
|
||||
class ApiUpdatePropertiesRequest extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"custom": {"name": "_custom", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"default": {"name": "_default", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"recompute": {"name": "_recompute", "type": TYPE_BOOL, "required": false},
|
||||
}
|
||||
|
||||
# Event custom properties.
|
||||
var _custom
|
||||
var custom : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _custom is Dictionary else _custom.duplicate()
|
||||
|
||||
# Event default properties.
|
||||
var _default
|
||||
var default : Dictionary:
|
||||
get:
|
||||
return Dictionary() if not _default is Dictionary else _default.duplicate()
|
||||
|
||||
# Informs the server to recompute the audience membership of the identity.
|
||||
var _recompute
|
||||
var recompute : bool:
|
||||
get:
|
||||
return false if not _recompute is bool else bool(_recompute)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUpdatePropertiesRequest:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ApiUpdatePropertiesRequest", p_dict), ApiUpdatePropertiesRequest) as ApiUpdatePropertiesRequest
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
if typeof(_custom) == TYPE_DICTIONARY:
|
||||
for k in _custom:
|
||||
map_string += "{%s=%s}, " % [k, _custom[k]]
|
||||
output += "custom: [%s], " % map_string
|
||||
map_string = ""
|
||||
if typeof(_default) == TYPE_DICTIONARY:
|
||||
for k in _default:
|
||||
map_string += "{%s=%s}, " % [k, _default[k]]
|
||||
output += "default: [%s], " % map_string
|
||||
map_string = ""
|
||||
output += "recompute: %s, " % _recompute
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
#
|
||||
class ProtobufAny extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"type": {"name": "_type", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
#
|
||||
var _type
|
||||
var type : String:
|
||||
get:
|
||||
return "" if not _type is String else String(_type)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ProtobufAny:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "ProtobufAny", p_dict), ProtobufAny) as ProtobufAny
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "type: %s, " % _type
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
#
|
||||
class RpcStatus extends SatoriAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"code": {"name": "_code", "type": TYPE_INT, "required": false},
|
||||
"details": {"name": "_details", "type": TYPE_ARRAY, "required": false, "content": TYPE_DICTIONARY},
|
||||
"message": {"name": "_message", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
#
|
||||
var _code
|
||||
var code : int:
|
||||
get:
|
||||
return 0 if not _code is int else int(_code)
|
||||
|
||||
#
|
||||
var _details
|
||||
var details : Array:
|
||||
get:
|
||||
return Array() if not _details is Array else Array(_details)
|
||||
|
||||
#
|
||||
var _message
|
||||
var message : String:
|
||||
get:
|
||||
return "" if not _message is String else String(_message)
|
||||
|
||||
func _init(p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> RpcStatus:
|
||||
return _safe_ret(SatoriSerializer.deserialize(p_ns, "RpcStatus", p_dict), RpcStatus) as RpcStatus
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return SatoriSerializer.serialize(self)
|
||||
|
||||
func _to_string() -> String:
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
var output : String = ""
|
||||
var map_string : String = ""
|
||||
output += "code: %s, " % _code
|
||||
output += "details: %s, " % [_details]
|
||||
output += "message: %s, " % _message
|
||||
output += map_string
|
||||
return output
|
||||
|
||||
# The low level client for the Satori API.
|
||||
class ApiClient extends RefCounted:
|
||||
|
||||
var _base_uri : String
|
||||
|
||||
var _http_adapter
|
||||
var _namespace : GDScript
|
||||
var _server_key : String
|
||||
var auto_refresh := true
|
||||
var auto_refresh_time := 300
|
||||
|
||||
var auto_retry : bool:
|
||||
set(p_value):
|
||||
_http_adapter.auto_retry = p_value
|
||||
get:
|
||||
return _http_adapter.auto_retry
|
||||
|
||||
var auto_retry_count : int:
|
||||
set(p_value):
|
||||
_http_adapter.auto_retry_count = p_value
|
||||
get:
|
||||
return _http_adapter.auto_retry_count
|
||||
|
||||
var auto_retry_backoff_base : int:
|
||||
set(p_value):
|
||||
_http_adapter.auto_retry_backoff_base = p_value
|
||||
get:
|
||||
return _http_adapter.auto_retry_backoff_base
|
||||
|
||||
var last_cancel_token:
|
||||
get:
|
||||
return _http_adapter.get_last_token()
|
||||
|
||||
func _init(p_base_uri : String, p_http_adapter, p_namespace : GDScript, p_server_key : String, p_timeout : int = 10):
|
||||
_base_uri = p_base_uri
|
||||
_http_adapter = p_http_adapter
|
||||
_http_adapter.timeout = p_timeout
|
||||
_namespace = p_namespace
|
||||
_server_key = p_server_key
|
||||
|
||||
|
||||
func _refresh_session(p_session : SatoriSession):
|
||||
if auto_refresh and p_session.is_valid() and p_session.refresh_token and not p_session.is_refresh_expired() and p_session.would_expire_in(auto_refresh_time):
|
||||
var request = ApiAuthenticateRefreshRequest.new()
|
||||
request._token = p_session.refresh_token
|
||||
return await authenticate_refresh_async(_server_key, "", request)
|
||||
return null
|
||||
|
||||
func cancel_request(p_token):
|
||||
if p_token:
|
||||
_http_adapter.cancel_request(p_token)
|
||||
|
||||
# A healthcheck which load balancers can use to check the service.
|
||||
func healthcheck_async(
|
||||
p_session : SatoriSession
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/healthcheck"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# A readycheck which load balancers can use to check the service.
|
||||
func readycheck_async(
|
||||
p_session : SatoriSession
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/readycheck"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# Authenticate against the server.
|
||||
func authenticate_async(
|
||||
p_basic_auth_username : String
|
||||
, p_basic_auth_password : String
|
||||
, p_body : ApiAuthenticateRequest
|
||||
) -> ApiSession:
|
||||
var urlpath : String = "/v1/authenticate"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "POST"
|
||||
var headers = {}
|
||||
var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
|
||||
var header = "Basic %s" % credentials
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiSession.new(result)
|
||||
var out : ApiSession = SatoriSerializer.deserialize(_namespace, "ApiSession", result)
|
||||
return out
|
||||
|
||||
# Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user.
|
||||
func authenticate_logout_async(
|
||||
p_session : SatoriSession
|
||||
, p_body : ApiAuthenticateLogoutRequest
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/authenticate/logout"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "POST"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# Refresh a user's session using a refresh token retrieved from a previous authentication request.
|
||||
func authenticate_refresh_async(
|
||||
p_basic_auth_username : String
|
||||
, p_basic_auth_password : String
|
||||
, p_body : ApiAuthenticateRefreshRequest
|
||||
) -> ApiSession:
|
||||
var urlpath : String = "/v1/authenticate/refresh"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "POST"
|
||||
var headers = {}
|
||||
var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
|
||||
var header = "Basic %s" % credentials
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiSession.new(result)
|
||||
var out : ApiSession = SatoriSerializer.deserialize(_namespace, "ApiSession", result)
|
||||
return out
|
||||
|
||||
# Publish an event for this session.
|
||||
func event_async(
|
||||
p_session : SatoriSession
|
||||
, p_body : ApiEventRequest
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/event"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "POST"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# Get or list all available experiments for this identity.
|
||||
func get_experiments_async(
|
||||
p_session : SatoriSession
|
||||
, p_names = null # : array
|
||||
) -> ApiExperimentList:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return ApiExperimentList.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/experiment"
|
||||
var query_params = ""
|
||||
if p_names != null:
|
||||
for elem in p_names:
|
||||
query_params += "names=%s&" % elem
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiExperimentList.new(result)
|
||||
var out : ApiExperimentList = SatoriSerializer.deserialize(_namespace, "ApiExperimentList", result)
|
||||
return out
|
||||
|
||||
# List all available flags for this identity.
|
||||
func get_flags_async(
|
||||
p_bearer_token : String
|
||||
, p_names = null # : array
|
||||
) -> ApiFlagList:
|
||||
var urlpath : String = "/v1/flag"
|
||||
var query_params = ""
|
||||
if p_names != null:
|
||||
for elem in p_names:
|
||||
query_params += "names=%s&" % elem
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
if (p_bearer_token):
|
||||
var header = "Bearer %s" % p_bearer_token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiFlagList.new(result)
|
||||
var out : ApiFlagList = SatoriSerializer.deserialize(_namespace, "ApiFlagList", result)
|
||||
return out
|
||||
|
||||
# Enrich/replace the current session with new identifier.
|
||||
func identify_async(
|
||||
p_session : SatoriSession
|
||||
, p_body : ApiIdentifyRequest
|
||||
) -> ApiSession:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return ApiSession.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/identify"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "PUT"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiSession.new(result)
|
||||
var out : ApiSession = SatoriSerializer.deserialize(_namespace, "ApiSession", result)
|
||||
return out
|
||||
|
||||
# Delete the caller's identity and associated data.
|
||||
func delete_identity_async(
|
||||
p_session : SatoriSession
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/identity"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "DELETE"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# List available live events.
|
||||
func get_live_events_async(
|
||||
p_session : SatoriSession
|
||||
, p_names = null # : array
|
||||
) -> ApiLiveEventList:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return ApiLiveEventList.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/live-event"
|
||||
var query_params = ""
|
||||
if p_names != null:
|
||||
for elem in p_names:
|
||||
query_params += "names=%s&" % elem
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiLiveEventList.new(result)
|
||||
var out : ApiLiveEventList = SatoriSerializer.deserialize(_namespace, "ApiLiveEventList", result)
|
||||
return out
|
||||
|
||||
# Get the list of messages for the identity.
|
||||
func get_message_list_async(
|
||||
p_session : SatoriSession
|
||||
, p_limit = null # : integer
|
||||
, p_forward = null # : boolean
|
||||
, p_cursor = null # : string
|
||||
) -> ApiGetMessageListResponse:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return ApiGetMessageListResponse.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/message"
|
||||
var query_params = ""
|
||||
if p_limit != null:
|
||||
query_params += "limit=%d&" % p_limit
|
||||
if p_forward != null:
|
||||
query_params += "forward=%s&" % str(bool(p_forward)).to_lower()
|
||||
if p_cursor != null:
|
||||
query_params += "cursor=%s&" % SatoriSerializer.escape_http(p_cursor)
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiGetMessageListResponse.new(result)
|
||||
var out : ApiGetMessageListResponse = SatoriSerializer.deserialize(_namespace, "ApiGetMessageListResponse", result)
|
||||
return out
|
||||
|
||||
# Deletes a message for an identity.
|
||||
func delete_message_async(
|
||||
p_session : SatoriSession
|
||||
, p_id : String
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/message/{id}"
|
||||
urlpath = urlpath.replace("{id}", SatoriSerializer.escape_http(p_id))
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "DELETE"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# Updates a message for an identity.
|
||||
func update_message_async(
|
||||
p_session : SatoriSession
|
||||
, p_id : String
|
||||
, p_body :
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/message/{id}"
|
||||
urlpath = urlpath.replace("{id}", SatoriSerializer.escape_http(p_id))
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "PUT"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
|
||||
# List properties associated with this identity.
|
||||
func list_properties_async(
|
||||
p_session : SatoriSession
|
||||
) -> ApiProperties:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return ApiProperties.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/properties"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "GET"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return ApiProperties.new(result)
|
||||
var out : ApiProperties = SatoriSerializer.deserialize(_namespace, "ApiProperties", result)
|
||||
return out
|
||||
|
||||
# Update identity properties.
|
||||
func update_properties_async(
|
||||
p_session : SatoriSession
|
||||
, p_body : ApiUpdatePropertiesRequest
|
||||
) -> SatoriAsyncResult:
|
||||
var try_refresh = await _refresh_session(p_session)
|
||||
if try_refresh != null:
|
||||
if try_refresh.is_exception():
|
||||
return SatoriAsyncResult.new(try_refresh.get_exception())
|
||||
await p_session.refresh(try_refresh)
|
||||
var urlpath : String = "/v1/properties"
|
||||
var query_params = ""
|
||||
var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
|
||||
var method = "PUT"
|
||||
var headers = {}
|
||||
var header = "Bearer %s" % p_session.token
|
||||
headers["Authorization"] = header
|
||||
|
||||
var content : PackedByteArray = PackedByteArray()
|
||||
content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
|
||||
|
||||
var result = await _http_adapter.send_async(method, uri, headers, content)
|
||||
if result is SatoriException:
|
||||
return SatoriAsyncResult.new(result)
|
||||
return SatoriAsyncResult.new()
|
||||
@@ -0,0 +1 @@
|
||||
uid://bjc75keigk02r
|
||||
@@ -0,0 +1,212 @@
|
||||
extends RefCounted
|
||||
|
||||
## A client for the API in Satori Server.
|
||||
class_name SatoriClient
|
||||
|
||||
#region Properties
|
||||
|
||||
var _host
|
||||
## The host address of the server.
|
||||
var host : String:
|
||||
get:
|
||||
return _host
|
||||
|
||||
var _port
|
||||
## The port number of the server.
|
||||
var port : int:
|
||||
get:
|
||||
return _port
|
||||
|
||||
var _scheme
|
||||
## The protocol scheme used to connect with the server. Must be either "http" or "https".
|
||||
var scheme : String:
|
||||
get:
|
||||
return _scheme
|
||||
|
||||
## The key used to authenticate with the server without a session.
|
||||
var api_key : String
|
||||
|
||||
## Set the timeout in seconds on requests sent to the server.
|
||||
var timeout : int
|
||||
|
||||
var _api_client : SatoriAPI.ApiClient
|
||||
|
||||
var auto_refresh : bool = false:
|
||||
set(v):
|
||||
set_auto_refresh(v)
|
||||
get:
|
||||
return get_auto_refresh()
|
||||
|
||||
func get_auto_refresh():
|
||||
return _api_client.auto_refresh
|
||||
|
||||
func set_auto_refresh(p_value):
|
||||
_api_client.auto_refresh = p_value
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
func _init(p_adapter : SatoriHTTPAdapter,
|
||||
p_api_key : String,
|
||||
p_scheme : String,
|
||||
p_host : String,
|
||||
p_port : int,
|
||||
p_timeout : int):
|
||||
|
||||
api_key = p_api_key
|
||||
_scheme = p_scheme
|
||||
_host = p_host
|
||||
_port = p_port
|
||||
timeout = p_timeout
|
||||
_api_client = SatoriAPI.ApiClient.new(_scheme + "://" + _host + ":" + str(_port), p_adapter, SatoriAPI, api_key, p_timeout)
|
||||
|
||||
#endregion
|
||||
|
||||
#region Client APIs
|
||||
|
||||
## Authenticate against the server.
|
||||
## [p_id]: An optional user id.
|
||||
## [p_default_properties]: Optional default properties to update with this call.
|
||||
## If not set, properties are left as they are on the server.
|
||||
## [p_custom_properties]: Optional custom properties to update with this call.
|
||||
## If not set, properties are left as they are on the server.
|
||||
func authenticate_async(p_id: String, p_default_properties: Dictionary = {}, p_custom_properties: Dictionary = {}) -> SatoriSession:
|
||||
return _parse_session(await _api_client.authenticate_async(api_key, "",
|
||||
SatoriAPI.ApiAuthenticateRequest.create(SatoriAPI, {
|
||||
"id": p_id,
|
||||
"default": p_default_properties,
|
||||
"custom": p_custom_properties
|
||||
})))
|
||||
|
||||
## Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user.
|
||||
## [p_session]: The session of the user.
|
||||
func authenticate_logout_async(p_session: SatoriSession) -> SatoriAsyncResult:
|
||||
return await _api_client.authenticate_logout_async(p_session,
|
||||
SatoriAPI.ApiAuthenticateLogoutRequest.create(SatoriAPI, {
|
||||
"refresh_token": p_session.refresh_token,
|
||||
"token": p_session.token
|
||||
}))
|
||||
|
||||
# Parses the Satori API session and returns a SatoriSession object.
|
||||
func _parse_session(p_session: SatoriAPI.ApiSession) -> SatoriSession:
|
||||
if p_session.is_exception():
|
||||
return SatoriSession.new(null, null, p_session.get_exception())
|
||||
|
||||
return SatoriSession.new(p_session.token, p_session.refresh_token)
|
||||
|
||||
## Refresh a user's session using a refresh token retrieved from a previous authentication request.
|
||||
## [p_sesison]: The session of the user.
|
||||
func session_refresh_async(p_session : SatoriSession) -> SatoriSession:
|
||||
return _parse_session(await _api_client.authenticate_refresh_async(api_key, "",
|
||||
SatoriAPI.ApiAuthenticateRefreshRequest.create(SatoriAPI, {
|
||||
"refresh_token": p_session.refresh_token,
|
||||
})
|
||||
))
|
||||
|
||||
## Send an event for this session.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_event]: The event which will be sent.
|
||||
func event_async(p_session: SatoriSession, p_event: Event) -> SatoriAsyncResult:
|
||||
return await events_async(p_session, [
|
||||
p_event
|
||||
])
|
||||
|
||||
## Send a batch of events for this session.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_events]: The batch of events which will be sent.
|
||||
func events_async(p_session: SatoriSession, p_events: Array) -> SatoriAsyncResult:
|
||||
var p_dict = {
|
||||
"events": p_events.map(func(e):
|
||||
return e.to_api_event_dict())
|
||||
}
|
||||
|
||||
var req = SatoriAPI.ApiEventRequest.create(SatoriAPI, p_dict)
|
||||
return await _api_client.event_async(p_session,
|
||||
req)
|
||||
|
||||
## Get all experiments data.
|
||||
## [p_session]: The session of the user.
|
||||
func get_all_experiments_async(p_session: SatoriSession) -> SatoriAsyncResult:
|
||||
return await _api_client.get_experiments_async(p_session)
|
||||
|
||||
## Get specific experiments data.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_names]: Experiment names.
|
||||
func get_experiments_async(p_session: SatoriSession, p_names: Array) -> SatoriAPI.ApiExperimentList:
|
||||
return await _api_client.get_experiments_async(p_session, p_names)
|
||||
|
||||
## Get a single flag for this identity.
|
||||
## This method will return the default value
|
||||
## specified and will not raise an exception if the network is unavailable
|
||||
## [p_session]: The session of the user.
|
||||
## [p_name]: The name of the flag.
|
||||
## [p_default]: The default value if the server is unreachable.
|
||||
func get_flag_async(p_session: SatoriSession, p_name: String, p_default: String = "") -> SatoriAPI.ApiFlag:
|
||||
var p_names = [p_name]
|
||||
var flags = await get_flags_async(p_session, p_names)
|
||||
|
||||
if flags.is_exception():
|
||||
return SatoriAPI.ApiFlag.create(SatoriAPI, {
|
||||
"name": p_name,
|
||||
"value": p_default
|
||||
})
|
||||
|
||||
for flag in flags.flags:
|
||||
if flag.name == p_name:
|
||||
return flag
|
||||
|
||||
return null
|
||||
|
||||
## List all available flags for this identity.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_names]: Flag names, if empty all flags will be returned.
|
||||
func get_flags_async(p_session: SatoriSession, p_names: Array) -> SatoriAPI.ApiFlagList:
|
||||
return await _api_client.get_flags_async(p_session.token, p_names)
|
||||
|
||||
## List available live events.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_names]: Live event names, if null or empty all live events are returned.
|
||||
func get_live_events_async(p_session: SatoriSession, p_names: Array = []) -> SatoriAPI.ApiLiveEventList:
|
||||
return await _api_client.get_live_events_async(p_session, p_names)
|
||||
|
||||
## Identify a session with a new ID.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_id]: Identity ID to enrich the current session and return a new session.
|
||||
## The old session will no longer be usable.
|
||||
## Must be between eight and 128 characters (inclusive).
|
||||
## Must be an alphanumeric string with only underscores and hyphens allowed.
|
||||
## [p_default_properties]: The default properties.
|
||||
## [p_custom_properties]: The custom event properties.
|
||||
func identify_async(p_session: SatoriSession, p_id: String, p_default_properties: Dictionary = {}, p_custom_properties: Dictionary = {}) -> SatoriSession:
|
||||
var req = SatoriAPI.ApiIdentifyRequest.create(SatoriAPI, {
|
||||
"id": p_id,
|
||||
"default": p_default_properties,
|
||||
"custom": p_custom_properties
|
||||
})
|
||||
return _parse_session(await _api_client.identify_async(p_session, req))
|
||||
|
||||
## List properties associated with this identity.
|
||||
## [p_session]: The session of the user.
|
||||
func list_properties_async(p_session: SatoriSession) -> SatoriAsyncResult:
|
||||
return await _api_client.list_properties_async(p_session)
|
||||
|
||||
## Update properties associated with this identity.
|
||||
## [p_session]: The session of the user.
|
||||
## [p_default_properties]: The default properties to update.
|
||||
## [p_custom_properties]: The custom properties to update.
|
||||
## [p_recompute]: Whether or not to recompute the user's audience membership immediately after property update.
|
||||
func update_properties_async(p_session: SatoriSession, p_default_properties: Dictionary, p_custom_properties: Dictionary, p_recompute: bool = false) -> SatoriAsyncResult:
|
||||
var req = SatoriAPI.ApiUpdatePropertiesRequest.create(SatoriAPI, {
|
||||
"default": p_default_properties,
|
||||
"custom": p_custom_properties,
|
||||
"recompute": p_recompute
|
||||
})
|
||||
return await _api_client.update_properties_async(p_session, req)
|
||||
|
||||
## Delete the caller's identity and associated data.
|
||||
## [p_session]: The session of the user.
|
||||
func delete_identity_async(p_session: SatoriSession) -> SatoriAsyncResult:
|
||||
return await _api_client.delete_identity_async(p_session)
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://dp2dpam3ffqar
|
||||
@@ -0,0 +1,208 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
# An adapter which implements the HTTP protocol.
|
||||
class_name SatoriHTTPAdapter
|
||||
|
||||
# The logger to use with the adapter.
|
||||
var logger : RefCounted = SatoriLogger.new()
|
||||
|
||||
# The timeout for requests
|
||||
var timeout : int = 3
|
||||
# If request should be automatically retried when a network error occurs.
|
||||
var auto_retry : bool = true
|
||||
# The maximum number of time a request will be retried when auto_retry is true
|
||||
var auto_retry_count : int = 3
|
||||
var auto_retry_backoff_base : int = 10
|
||||
# Whether or not to use threads when making HTTP requests.
|
||||
var use_threads : bool = true
|
||||
|
||||
var _pending = {}
|
||||
var id : int = 0
|
||||
|
||||
class AsyncRequest:
|
||||
var id : int
|
||||
var request : HTTPRequest
|
||||
var uri : String
|
||||
var method : int
|
||||
var headers : PackedStringArray
|
||||
var body : PackedByteArray
|
||||
var retry_count := 3
|
||||
var backoff_time := 10
|
||||
var logger : SatoriLogger
|
||||
|
||||
var cancelled = false
|
||||
var result : int = HTTPRequest.RESULT_NO_RESPONSE
|
||||
var response_code : int = -1
|
||||
var response_body : PackedByteArray
|
||||
var timer : SceneTreeTimer = null
|
||||
var cur_try : int = 1
|
||||
var rng = RandomNumberGenerator.new()
|
||||
|
||||
func _init(p_id : int, p_request : HTTPRequest, p_uri : String,
|
||||
p_method : int, p_headers : PackedStringArray, p_body : PackedByteArray,
|
||||
p_retry_count : int, p_backoff_time : int, p_logger : SatoriLogger):
|
||||
rng.seed = Time.get_ticks_usec()
|
||||
id = p_id
|
||||
request = p_request
|
||||
uri = p_uri
|
||||
method = p_method
|
||||
headers = p_headers
|
||||
body = p_body
|
||||
retry_count = p_retry_count
|
||||
backoff_time = p_backoff_time
|
||||
logger = p_logger
|
||||
|
||||
func should_retry():
|
||||
return cur_try < retry_count and not cancelled
|
||||
|
||||
func retry():
|
||||
var time = pow(backoff_time, cur_try) * rng.randf_range(0.5, 1)
|
||||
logger.debug("Retrying request %d. Tries left: %d. Backoff: %d ms" % [
|
||||
id, retry_count - cur_try, time
|
||||
])
|
||||
cur_try += 1
|
||||
await backoff(time)
|
||||
if cancelled:
|
||||
return
|
||||
return await make_request()
|
||||
|
||||
func make_request():
|
||||
var err = request.request(uri, headers, method, body.get_string_from_utf8())
|
||||
if err != OK:
|
||||
await request.get_tree().process_frame
|
||||
result = HTTPRequest.RESULT_CANT_CONNECT
|
||||
logger.debug("Request %d failed to start, error: %d" % [id, err])
|
||||
return
|
||||
|
||||
var args = await request.request_completed
|
||||
result = args[0]
|
||||
response_code = args[1]
|
||||
response_body = args[3]
|
||||
|
||||
func backoff(p_time : int):
|
||||
timer = request.get_tree().create_timer(p_time / 1000.0)
|
||||
await timer.timeout
|
||||
timer = null
|
||||
|
||||
func cancel():
|
||||
cancelled = true
|
||||
request.cancel_request()
|
||||
if timer:
|
||||
timer.time_left = 0
|
||||
else:
|
||||
request.call_deferred("emit_signal", "request_completed", HTTPRequest.RESULT_REQUEST_FAILED, 0, [], [])
|
||||
|
||||
func parse_result():
|
||||
if cancelled:
|
||||
return SatoriException.new("Request cancelled", -1, -1, true)
|
||||
elif result != HTTPRequest.RESULT_SUCCESS:
|
||||
if result == null:
|
||||
result = 0
|
||||
return SatoriException.new("HTTPRequest failed!", result)
|
||||
|
||||
var json = JSON.new()
|
||||
|
||||
var json_error = json.parse(response_body.get_string_from_utf8())
|
||||
if json_error != OK:
|
||||
logger.debug("Unable to parse request %d response. JSON error: %d, JSON error message: %s, response code: %d" % [
|
||||
id, json_error, json.get_error_message(), response_code
|
||||
])
|
||||
return SatoriException.new("Failed to decode JSON response", response_code)
|
||||
|
||||
var parsed = json.get_data()
|
||||
|
||||
if response_code != HTTPClient.RESPONSE_OK:
|
||||
var error = ""
|
||||
var code = -1
|
||||
if typeof(parsed) == TYPE_DICTIONARY:
|
||||
if "message" in parsed:
|
||||
error = parsed["message"]
|
||||
elif "error" in parsed:
|
||||
error = parsed["error"]
|
||||
else:
|
||||
error = str(parsed)
|
||||
code = parsed["code"] if "code" in parsed else -1
|
||||
else:
|
||||
error = str(parsed)
|
||||
if typeof(error) == TYPE_DICTIONARY:
|
||||
error = JSON.stringify(error)
|
||||
logger.debug("Request %d returned response code: %d, RPC code: %d, error: %s" % [
|
||||
id, response_code, code, error
|
||||
])
|
||||
return SatoriException.new(error, response_code, code)
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
# Send a HTTP request.
|
||||
# @param method - HTTP method to use for this request.
|
||||
# @param uri - The fully qualified URI to use.
|
||||
# @param headers - Request headers to set.
|
||||
# @param body - Request content body to set.
|
||||
# @param timeoutSec - Request timeout.
|
||||
# Returns a task which resolves to the contents of the response.
|
||||
func send_async(p_method : String, p_uri : String, p_headers : Dictionary, p_body : PackedByteArray):
|
||||
var req = HTTPRequest.new()
|
||||
req.timeout = timeout
|
||||
if use_threads and OS.get_name() != 'Web':
|
||||
req.use_threads = true # Threads not available nor needed on the web.
|
||||
|
||||
# Parse method
|
||||
var method = HTTPClient.METHOD_GET
|
||||
if p_method == "POST":
|
||||
method = HTTPClient.METHOD_POST
|
||||
elif p_method == "PUT":
|
||||
method = HTTPClient.METHOD_PUT
|
||||
elif p_method == "DELETE":
|
||||
method = HTTPClient.METHOD_DELETE
|
||||
elif p_method == "HEAD":
|
||||
method = HTTPClient.METHOD_HEAD
|
||||
var headers = PackedStringArray()
|
||||
|
||||
# Parse headers
|
||||
headers.append("Accept: application/json")
|
||||
for k in p_headers:
|
||||
headers.append("%s: %s" % [k, p_headers[k]])
|
||||
|
||||
id += 1
|
||||
var retry = auto_retry_count if auto_retry else 0
|
||||
var backoff = auto_retry_backoff_base
|
||||
_pending[id] = AsyncRequest.new(id, req, p_uri, method, headers, p_body, retry, backoff, logger)
|
||||
|
||||
logger.debug("Sending request [ID: %d, Method: %s, Uri: %s, Headers: %s, Body: %s, Timeout: %d, Retries: %d, Backoff base: %d ms]" % [
|
||||
id, p_method, p_uri, p_headers, p_body.get_string_from_utf8(), timeout, retry, backoff
|
||||
])
|
||||
|
||||
add_child(req)
|
||||
|
||||
return await _send_async(id, _pending)
|
||||
|
||||
func get_last_token():
|
||||
return id
|
||||
|
||||
func cancel_request(p_token):
|
||||
if _pending.has(p_token):
|
||||
_pending[p_token].cancel()
|
||||
|
||||
static func _clear_request(p_request : AsyncRequest, p_pending : Dictionary, p_id : int):
|
||||
if not p_request.request.is_queued_for_deletion():
|
||||
p_request.logger.debug("Freeing request %d" % p_id)
|
||||
p_request.request.queue_free()
|
||||
p_pending.erase(p_id)
|
||||
|
||||
static func _send_async(p_id : int, p_pending : Dictionary):
|
||||
|
||||
var req : AsyncRequest = p_pending[p_id]
|
||||
await req.make_request()
|
||||
|
||||
while req.result != HTTPRequest.RESULT_SUCCESS:
|
||||
req.logger.debug("Request %d failed with result: %d, response code: %d" % [
|
||||
p_id, req.result, req.response_code
|
||||
])
|
||||
if not req.should_retry():
|
||||
break
|
||||
await req.retry()
|
||||
|
||||
_clear_request(req, p_pending, p_id)
|
||||
return req.parse_result()
|
||||
@@ -0,0 +1 @@
|
||||
uid://b7i4l7xvmg5qe
|
||||
@@ -0,0 +1,107 @@
|
||||
extends SatoriAsyncResult
|
||||
class_name SatoriSession
|
||||
|
||||
var _token: String = ""
|
||||
var token: String:
|
||||
get:
|
||||
return _token
|
||||
|
||||
var _refresh_token: String = ""
|
||||
var refresh_token: String:
|
||||
get:
|
||||
return _refresh_token
|
||||
|
||||
var _expire_time: int = 0
|
||||
var expire_time: int:
|
||||
get:
|
||||
return _expire_time
|
||||
|
||||
var expired: bool:
|
||||
get:
|
||||
return is_expired()
|
||||
|
||||
var _refresh_expire_time: int = 0
|
||||
var refresh_expire_time: int:
|
||||
get:
|
||||
return _refresh_expire_time
|
||||
|
||||
var _identity_id: String = ""
|
||||
var identity_id: String:
|
||||
get:
|
||||
return _identity_id
|
||||
|
||||
var _valid : bool = false
|
||||
var valid : bool:
|
||||
get:
|
||||
return _valid
|
||||
|
||||
func is_expired() -> bool:
|
||||
return _expire_time < Time.get_unix_time_from_system()
|
||||
|
||||
func would_expire_in(p_secs : int) -> bool:
|
||||
return _expire_time < Time.get_unix_time_from_system() + p_secs
|
||||
|
||||
func has_refresh_expired(offset: float) -> bool:
|
||||
return _expire_time < offset
|
||||
|
||||
func is_refresh_expired() -> bool:
|
||||
return _refresh_expire_time < Time.get_unix_time_from_system()
|
||||
|
||||
func is_valid():
|
||||
return _valid
|
||||
|
||||
# Initializes a new instance of the SatoriSession class.
|
||||
#
|
||||
# @param p_token - The authentication token.
|
||||
# @param p_refresh_token - The refresh token.
|
||||
# @param p_exception - The exception to be thrown, if any.
|
||||
func _init(p_token = null, p_refresh_token = null, p_exception = null):
|
||||
super(p_exception)
|
||||
|
||||
_refresh_expire_time = 0
|
||||
if p_token:
|
||||
_update(p_token, p_refresh_token)
|
||||
|
||||
func _update(p_token, p_refresh_token):
|
||||
_token = p_token
|
||||
_refresh_token = p_refresh_token
|
||||
|
||||
var decoded = _jwt_unpack(p_token)
|
||||
if decoded.is_empty():
|
||||
_valid = false
|
||||
return
|
||||
|
||||
_valid = true
|
||||
_expire_time = int(decoded.get("exp", 0))
|
||||
_identity_id = str(decoded.get("iid", ""))
|
||||
_refresh_expire_time = int(_jwt_unpack(refresh_token).get("exp", 0)) if !refresh_token.is_empty() else 0
|
||||
|
||||
func _to_string():
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
|
||||
return "Session<AuthToken=%s, ExpireTime=%d, RefreshToken=%s, RefreshExpireTime=%d, IdentityId=%s>" % [
|
||||
_token, _expire_time, _refresh_token, _refresh_expire_time, identity_id]
|
||||
|
||||
func _jwt_unpack(p_token : String) -> Dictionary:
|
||||
# Hack decode JSON payload from JWT.
|
||||
if p_token.find(".") == -1:
|
||||
_ex = SatoriException.new("Missing payload: %s" % p_token)
|
||||
return {}
|
||||
var payload = p_token.split('.')[1];
|
||||
var pad_length = ceil(payload.length() / 4.0) * 4;
|
||||
# Pad base64
|
||||
for i in range(0, pad_length - payload.length()):
|
||||
payload += "="
|
||||
payload = payload.replace("-", "+").replace("_", "/")
|
||||
var unpacked = Marshalls.base64_to_utf8(payload)
|
||||
|
||||
var json = JSON.new()
|
||||
var error = json.parse(unpacked)
|
||||
|
||||
if error == OK:
|
||||
var decoded = json.get_data()
|
||||
if typeof(decoded) == TYPE_DICTIONARY:
|
||||
return decoded
|
||||
_ex = SatoriException.new("Unable to unpack token: %s" % p_token)
|
||||
return {}
|
||||
@@ -0,0 +1 @@
|
||||
uid://yreiavija6v4
|
||||
@@ -0,0 +1,34 @@
|
||||
extends RefCounted
|
||||
class_name SatoriAsyncResult
|
||||
|
||||
var exception : SatoriException:
|
||||
set(v):
|
||||
pass
|
||||
get:
|
||||
return get_exception()
|
||||
|
||||
var _ex = null
|
||||
|
||||
func _init(p_ex = null):
|
||||
_ex = p_ex
|
||||
|
||||
func is_exception():
|
||||
return get_exception() != null
|
||||
|
||||
func was_cancelled():
|
||||
return is_exception() and get_exception().cancelled
|
||||
|
||||
func get_exception() -> SatoriException:
|
||||
return _ex as SatoriException
|
||||
|
||||
func _to_string():
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
return "SatoriAsyncResult<>"
|
||||
|
||||
static func _safe_ret(p_obj, p_type : GDScript):
|
||||
if is_instance_of(p_obj, p_type):
|
||||
return p_obj
|
||||
elif p_obj is SatoriException:
|
||||
return p_type.new(p_obj)
|
||||
return p_type.new(SatoriException.new())
|
||||
@@ -0,0 +1 @@
|
||||
uid://dhrty7nqady0k
|
||||
@@ -0,0 +1,42 @@
|
||||
extends RefCounted
|
||||
|
||||
# An exception generated during a request.
|
||||
# Usually contains at least an error message.
|
||||
class_name SatoriException
|
||||
|
||||
var _status_code : int = -1
|
||||
var status_code : int:
|
||||
set(v):
|
||||
pass
|
||||
get:
|
||||
return _status_code
|
||||
|
||||
var _grpc_status_code : int = -1
|
||||
var grpc_status_code : int:
|
||||
set(v):
|
||||
pass
|
||||
get:
|
||||
return _grpc_status_code
|
||||
|
||||
var _message : String = ""
|
||||
var message : String:
|
||||
set(v):
|
||||
pass
|
||||
get:
|
||||
return _message
|
||||
|
||||
var _cancelled : bool = false
|
||||
var cancelled : bool:
|
||||
set(v):
|
||||
pass
|
||||
get:
|
||||
return _cancelled
|
||||
|
||||
func _init(p_message : String = "", p_status_code : int = -1, p_grpc_status_code : int = -1, p_cancelled : bool = false):
|
||||
_status_code = p_status_code
|
||||
_grpc_status_code = p_grpc_status_code
|
||||
_message = p_message
|
||||
_cancelled = p_cancelled
|
||||
|
||||
func _to_string() -> String:
|
||||
return "SatoriException(StatusCode={%s}, Message='{%s}', GrpcStatusCode={%s})" % [_status_code, _message, _grpc_status_code]
|
||||
@@ -0,0 +1 @@
|
||||
uid://ds56ldehv1e2i
|
||||
@@ -0,0 +1,38 @@
|
||||
extends RefCounted
|
||||
class_name SatoriLogger
|
||||
|
||||
enum LOG_LEVEL {NONE, ERROR, WARNING, INFO, VERBOSE, DEBUG}
|
||||
|
||||
var _level = LOG_LEVEL.ERROR
|
||||
var _module = "Satori"
|
||||
|
||||
func _init(p_module : String = "Satori", p_level : int = LOG_LEVEL.ERROR):
|
||||
_level = p_level
|
||||
_module = p_module
|
||||
|
||||
func _log(level : int, msg):
|
||||
if level <= _level:
|
||||
if level == LOG_LEVEL.ERROR:
|
||||
printerr("=== %s : ERROR === %s" % [_module, str(msg)])
|
||||
else:
|
||||
var what = "=== UNKNOWN === "
|
||||
for k in LOG_LEVEL:
|
||||
if level == LOG_LEVEL[k]:
|
||||
what = "=== %s : %s === " % [_module, k]
|
||||
break
|
||||
print(what + str(msg))
|
||||
|
||||
func error(msg):
|
||||
_log(LOG_LEVEL.ERROR, msg)
|
||||
|
||||
func warning(msg):
|
||||
_log(LOG_LEVEL.WARNING, msg)
|
||||
|
||||
func info(msg):
|
||||
_log(LOG_LEVEL.INFO, msg)
|
||||
|
||||
func verbose(msg):
|
||||
_log(LOG_LEVEL.VERBOSE, msg)
|
||||
|
||||
func debug(msg):
|
||||
_log(LOG_LEVEL.DEBUG, msg)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dk3qpgkfd74x0
|
||||
@@ -0,0 +1,163 @@
|
||||
extends RefCounted
|
||||
class_name SatoriSerializer
|
||||
|
||||
static func serialize(p_obj : Object) -> Dictionary:
|
||||
var out = {}
|
||||
var schema = p_obj.get("_SCHEMA")
|
||||
if schema == null:
|
||||
return {} # No schema defined
|
||||
for k in schema:
|
||||
var prop = schema[k]
|
||||
var val = p_obj.get(prop["name"])
|
||||
if val == null:
|
||||
continue
|
||||
var type = prop["type"]
|
||||
var content = prop.get("content", TYPE_NIL)
|
||||
if typeof(content) == TYPE_STRING:
|
||||
content = TYPE_OBJECT
|
||||
var val_type = typeof(val)
|
||||
match val_type:
|
||||
TYPE_OBJECT: # Simple objects
|
||||
out[k] = serialize(val)
|
||||
TYPE_ARRAY: # Array of objects
|
||||
var arr = []
|
||||
if val.size() > 0 and typeof(val[0]) == TYPE_OBJECT: # Array of objects
|
||||
for e in val:
|
||||
arr.append(serialize(e))
|
||||
else:
|
||||
arr = val
|
||||
out[k] = arr
|
||||
TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_STRING_ARRAY: # Array of ints, bools, or strings
|
||||
var arr = []
|
||||
for e in val:
|
||||
if content == TYPE_BOOL:
|
||||
e = bool(e)
|
||||
if typeof(e) != content:
|
||||
continue
|
||||
arr.append(e)
|
||||
out[k] = arr
|
||||
TYPE_DICTIONARY: # Maps
|
||||
var dict = {}
|
||||
if content == TYPE_OBJECT: # Map of objects
|
||||
for l in val:
|
||||
if typeof(val[l]) != TYPE_OBJECT:
|
||||
continue
|
||||
dict[l] = serialize(val[l])
|
||||
else: # Map of simple types
|
||||
for l in val:
|
||||
var e = val[l]
|
||||
if content == TYPE_FLOAT:
|
||||
e = float(e)
|
||||
elif content == TYPE_INT:
|
||||
e = int(e)
|
||||
elif content == TYPE_BOOL:
|
||||
e = bool(e)
|
||||
if typeof(e) != content:
|
||||
continue
|
||||
dict[l] = e
|
||||
out[k] = dict
|
||||
_:
|
||||
out[k] = val
|
||||
return out
|
||||
|
||||
static func deserialize(p_ns : GDScript, p_cls_name : String, p_dict : Dictionary) -> Object:
|
||||
var cls : GDScript = p_ns.get(p_cls_name)
|
||||
var schema = cls.get("_SCHEMA")
|
||||
if schema == null:
|
||||
return SatoriException.new() # No schema defined
|
||||
var obj = cls.new()
|
||||
for k in schema:
|
||||
var prop = schema[k]
|
||||
var pname = prop["name"]
|
||||
var type = prop["type"]
|
||||
var required = prop["required"]
|
||||
var content = prop.get("content", TYPE_NIL)
|
||||
var type_cmp = type
|
||||
if typeof(type) == TYPE_STRING: # A class
|
||||
type_cmp = TYPE_DICTIONARY
|
||||
if type_cmp == TYPE_PACKED_STRING_ARRAY or type_cmp == TYPE_PACKED_INT32_ARRAY: # A specialized array
|
||||
type_cmp = TYPE_ARRAY
|
||||
|
||||
var content_cmp = content
|
||||
if typeof(content) == TYPE_STRING: # A dictionary or array of classes
|
||||
content_cmp = TYPE_DICTIONARY
|
||||
|
||||
var val = p_dict.get(k, null)
|
||||
|
||||
# Ints might and up being recognized as floats. Change that if needed
|
||||
if type_cmp == TYPE_INT:
|
||||
if typeof(val) == TYPE_FLOAT:
|
||||
val = int(val)
|
||||
elif typeof(val) == TYPE_STRING and val.is_valid_int():
|
||||
val = val.to_int()
|
||||
|
||||
if typeof(val) == type_cmp:
|
||||
if typeof(type) == TYPE_STRING:
|
||||
obj.set(pname, deserialize(p_ns, type, val))
|
||||
elif type_cmp == TYPE_DICTIONARY:
|
||||
var v = {}
|
||||
for l in val:
|
||||
if typeof(content) == TYPE_STRING:
|
||||
v[l] = deserialize(p_ns, content, val[l])
|
||||
elif content == TYPE_FLOAT:
|
||||
v[l] = float(val[l])
|
||||
elif content == TYPE_INT:
|
||||
v[l] = int(val[l])
|
||||
elif content == TYPE_BOOL:
|
||||
v[l] = bool(val[l])
|
||||
else:
|
||||
v[l] = str(val[l])
|
||||
obj.set(pname, v)
|
||||
elif type_cmp == TYPE_ARRAY:
|
||||
var v
|
||||
match content:
|
||||
TYPE_INT, TYPE_BOOL: v = PackedInt32Array()
|
||||
TYPE_STRING: v = PackedStringArray()
|
||||
_: v = Array()
|
||||
for e in val:
|
||||
if typeof(e) == TYPE_DICTIONARY:
|
||||
v.append(e) # Avoid deserialization if e is already a dictionary
|
||||
elif typeof(content) == TYPE_STRING:
|
||||
v.append(deserialize(p_ns, content, e))
|
||||
elif content == TYPE_FLOAT:
|
||||
v.append(float(e))
|
||||
elif content == TYPE_INT:
|
||||
v.append(int(e))
|
||||
elif content == TYPE_BOOL:
|
||||
v.append(bool(e))
|
||||
else:
|
||||
v.append(str(e))
|
||||
obj.set(pname, v)
|
||||
else:
|
||||
obj.set(pname, val)
|
||||
elif required:
|
||||
obj._ex = SatoriException.new("ERROR [%s]: Missing or invalid required prop %s = %s:\n\t%s" % [p_cls_name, prop, p_dict.get(k), p_dict])
|
||||
return obj
|
||||
return obj
|
||||
|
||||
|
||||
###
|
||||
# Compatibility with Godot 3.1 which does not expose String.http_escape
|
||||
###
|
||||
const HEX = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
|
||||
|
||||
static func escape_http(p_str : String) -> String:
|
||||
var out : String = ""
|
||||
for o in p_str:
|
||||
if (o == '.' or o == '-' or o == '_' or o == '~' or
|
||||
(o >= 'a' and o <= 'z') or
|
||||
(o >= 'A' and o <= 'Z') or
|
||||
(o >= '0' and o <= '9')):
|
||||
out += o
|
||||
else:
|
||||
for b in o.to_utf8_buffer():
|
||||
out += "%%%s" % to_hex(b)
|
||||
return out
|
||||
|
||||
static func to_hex(p_val : int) -> String:
|
||||
var v := p_val
|
||||
var o := ""
|
||||
while v != 0:
|
||||
o = HEX[v % 16] + o
|
||||
v /= 16
|
||||
return o
|
||||
@@ -0,0 +1 @@
|
||||
uid://dcgodt1wuq4ei
|
||||
Reference in New Issue
Block a user