Client Configurations + Auto-Retry on HTTP Request Failure #31

Merged
MarciiTheDev merged 3 commits from feature/client-configuration into dev 2024-05-11 15:22:10 +00:00
7 changed files with 83 additions and 29 deletions

View File

@ -8,31 +8,33 @@ local tokens = {
local Utils = require(script.Parent.Utils) local Utils = require(script.Parent.Utils)
local MarcSyncClient = {} local MarcSyncClient = {}
local Types = require(script.Parent.Types)
MarcSyncClient.getVersion = function(self:typeof(MarcSyncClient), clientId: number?):string MarcSyncClient.getVersion = function(self:typeof(MarcSyncClient), clientId: number?):string
self:_checkInstallation() self:_checkInstallation()
local url = "" local url = ""
if clientId then url = "/"..clientId end if clientId then url = "/"..clientId end
local result = Utils.makeHTTPRequest("GET", "https://api.marcsync.dev/v0/utils/version"..url); local result = Utils.makeHTTPRequest("", "GET", "https://api.marcsync.dev/v0/utils/version"..url, nil, nil, self._options);
return result["version"] return result["version"]
end end
MarcSyncClient.createCollection = function(self:typeof(MarcSyncClient), collectionName: string):typeof(require(script.Parent.Objects.Collection).new()) MarcSyncClient.createCollection = function(self:typeof(MarcSyncClient), collectionName: string):typeof(require(script.Parent.Objects.Collection).new())
if not self._accessToken then error("[MarcSync] Please set a Token before using MarcSync.") end if not self._accessToken then error("[MarcSync] Please set a Token before using MarcSync.") end
if not collectionName then error("No CollectionName Provided") end if not collectionName then error("No CollectionName Provided") end
local result = Utils.makeHTTPRequest("collection", "POST", "https://api.marcsync.dev/v0/collection/"..collectionName, {}, self._accessToken); local result = Utils.makeHTTPRequest("collection", "POST", "https://api.marcsync.dev/v0/collection/"..collectionName, {}, self._accessToken, self._options);
if not result["success"] then error(result["errorMessage"]) end if not result["success"] then error(result["errorMessage"]) end
result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken) result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken, self._options)
return result return result
end end
MarcSyncClient.fetchCollection = function(self:typeof(MarcSyncClient), collectionName: string):typeof(require(script.Parent.Objects.Collection).new()) MarcSyncClient.fetchCollection = function(self:typeof(MarcSyncClient), collectionName: string):typeof(require(script.Parent.Objects.Collection).new())
self:_checkInstallation() self:_checkInstallation()
if not collectionName then error("No CollectionName Provided") end if not collectionName then error("No CollectionName Provided") end
local result = Utils.makeHTTPRequest("collection", "GET", "https://api.marcsync.dev/v0/collection/"..collectionName, {}, self._accessToken); local result = Utils.makeHTTPRequest("collection", "GET", "https://api.marcsync.dev/v0/collection/"..collectionName, {}, self._accessToken, self._options);
if not result["success"] then error(result["errorMessage"]) end if not result["success"] then error(result["errorMessage"]) end
result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken) result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken, self._options)
return result return result
end end
@ -40,15 +42,16 @@ MarcSyncClient.getCollection = function(self:typeof(MarcSyncClient), collectionN
if typeof(self) ~= "table" then error("Please use : instead of .") end if typeof(self) ~= "table" then error("Please use : instead of .") end
self:_checkInstallation() self:_checkInstallation()
if not collectionName then error("No CollectionName Provided") end if not collectionName then error("No CollectionName Provided") end
return require(script.Parent.Objects.Collection).new(collectionName, self._accessToken) return require(script.Parent.Objects.Collection).new(collectionName, self._accessToken, self._options)
end end
return { return {
new = function(accessToken: string):typeof(MarcSyncClient) new = function(accessToken: string, options: Types.ClientOptions?):typeof(MarcSyncClient)
if not accessToken then warn("Token not provided while creating a new MarcSync Object.") end if not accessToken then warn("Token not provided while creating a new MarcSync Object.") end
if not tokens[accessToken] then warn("Token provided for creating a new MarcSync Object not Found in Token Table, using it as token instead.") else accessToken = tokens[accessToken] end if not tokens[accessToken] then warn("Token provided for creating a new MarcSync Object not Found in Token Table, using it as token instead.") else accessToken = tokens[accessToken] end
local self = {} local self = {}
self._accessToken = accessToken self._accessToken = accessToken
self._options = options or {retryCount = 3}
self._checkInstallation = function() self._checkInstallation = function()
if not self then error("Please Setup MarcSync before using MarcSync.") end if not self then error("Please Setup MarcSync before using MarcSync.") end
if not self._accessToken then error("[MarcSync] Please set a Token before using MarcSync.") end if not self._accessToken then error("[MarcSync] Please set a Token before using MarcSync.") end

View File

@ -7,11 +7,11 @@ local Collection = {}
Collection.createEntry = function(self:typeof(Collection), data:Types.EntryData):typeof(Entry.new()) Collection.createEntry = function(self:typeof(Collection), data:Types.EntryData):typeof(Entry.new())
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = Utils.makeHTTPRequest("entry", "POST", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["data"]=data}, self._accessToken); local result = Utils.makeHTTPRequest("entry", "POST", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["data"]=data}, self._accessToken, self._options);
if result["success"] and result["objectId"] then if result["success"] and result["objectId"] then
data["_id"] = result["objectId"] data["_id"] = result["objectId"]
result = require(script.Parent.Entry).new(self._collectionName, data, self._accessToken) result = require(script.Parent.Entry).new(self._collectionName, data, self._accessToken, self._options)
else else
error(result["errorMessage"]) error(result["errorMessage"])
end end
@ -21,7 +21,7 @@ end
Collection.updateEntries = function(self:typeof(Collection), filters:Types.EntryData, data:Types.EntryData):number Collection.updateEntries = function(self:typeof(Collection), filters:Types.EntryData, data:Types.EntryData):number
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = Utils.makeHTTPRequest("entry", "PUT", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["filters"]=filters,["data"]=data}, self._accessToken); local result = Utils.makeHTTPRequest("entry", "PUT", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["filters"]=filters,["data"]=data}, self._accessToken, self._options);
if not result["success"] then error(result["errorMessage"]) end if not result["success"] then error(result["errorMessage"]) end
return result["modifiedEntries"] return result["modifiedEntries"]
@ -30,11 +30,11 @@ end
Collection.getEntries = function(self:typeof(Collection), filters:Types.EntryData):{[number]:typeof(Entry.new())} Collection.getEntries = function(self:typeof(Collection), filters:Types.EntryData):{[number]:typeof(Entry.new())}
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
if not filters then filters = {} end if not filters then filters = {} end
local result = Utils.makeHTTPRequest("entry", "DELETE", "https://api.marcsync.dev/v0/entries/"..self._collectionName.."?isQuery=true", {["filters"]=filters}, self._accessToken); local result = Utils.makeHTTPRequest("entry", "DELETE", "https://api.marcsync.dev/v0/entries/"..self._collectionName.."?isQuery=true", {["filters"]=filters}, self._accessToken, self._options);
if result["success"] and result["entries"] then if result["success"] and result["entries"] then
local _result = {} local _result = {}
for index,entry in pairs(result["entries"]) do for index,entry in pairs(result["entries"]) do
_result[index] = require(script.Parent.Entry).new(self._collectionName, entry, self._accessToken) _result[index] = require(script.Parent.Entry).new(self._collectionName, entry, self._accessToken, self._options)
end end
result = _result result = _result
else else
@ -46,7 +46,7 @@ end
Collection.deleteEntries = function(self:typeof(Collection), filters:Types.EntryData):number Collection.deleteEntries = function(self:typeof(Collection), filters:Types.EntryData):number
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = Utils.makeHTTPRequest("DELETE", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["filters"]=filters}, self._accessToken); local result = Utils.makeHTTPRequest("DELETE", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["filters"]=filters}, self._accessToken, self._options);
if not result["success"] then error(result["errorMessage"]) end if not result["success"] then error(result["errorMessage"]) end
return result["deletedEntries"] return result["deletedEntries"]
@ -54,16 +54,17 @@ end
Collection.drop = function(self:typeof(Collection)) Collection.drop = function(self:typeof(Collection))
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = Utils.makeHTTPRequest("collection", "DELETE", "https://api.marcsync.dev/v0/collection/"..self._collectionName, {}, self._accessToken); local result = Utils.makeHTTPRequest("collection", "DELETE", "https://api.marcsync.dev/v0/collection/"..self._collectionName, {}, self._accessToken, self._options);
if not result["success"] then error(result["errorMessage"]) end if not result["success"] then error(result["errorMessage"]) end
self = nil self = nil
end end
return { return {
new = function(collectionName: string, accessToken: string):typeof(Collection) new = function(collectionName: string, accessToken: string, options: Types.ClientOptions):typeof(Collection)
local self = {} local self = {}
self._collectionName = collectionName self._collectionName = collectionName
self._accessToken = accessToken self._accessToken = accessToken
self._options = options
self = setmetatable(self, { self = setmetatable(self, {
__index = Collection __index = Collection

View File

@ -14,7 +14,7 @@ Entry.getValues = function(self:typeof(Entry)):Types.EntryData
end end
Entry.updateValues = function(self:typeof(Entry), data:Types.EntryData):number Entry.updateValues = function(self:typeof(Entry), data:Types.EntryData):number
local result = Utils.makeHTTPRequest("entry", "PUT", "https://api.marcsync.dev/v0/entries/"..self._tableId, {["filters"]={["_id"]=self._objectId},["data"]=data}, self._accessToken); local result = Utils.makeHTTPRequest("entry", "PUT", "https://api.marcsync.dev/v0/entries/"..self._tableId, {["filters"]={["_id"]=self._objectId},["data"]=data}, self._accessToken, self._options);
if result["success"] and result["modifiedEntries"] and result["modifiedEntries"] > 0 then if result["success"] and result["modifiedEntries"] and result["modifiedEntries"] > 0 then
for i,v in pairs(data) do for i,v in pairs(data) do
@ -29,7 +29,7 @@ end
Entry.delete = function(self:typeof(Entry)) Entry.delete = function(self:typeof(Entry))
if typeof(self) ~= "table" then error("Please use : instead of .") end if typeof(self) ~= "table" then error("Please use : instead of .") end
local result = Utils.makeHTTPRequest("entry", "DELETE", "https://api.marcsync.dev/v0/entries/"..self._tableId, {["filters"]={["_id"]=self._objectId}}, self._accessToken); local result = Utils.makeHTTPRequest("entry", "DELETE", "https://api.marcsync.dev/v0/entries/"..self._tableId, {["filters"]={["_id"]=self._objectId}}, self._accessToken, self._options);
if not result["success"] then error(result["errorMessage"]) end if not result["success"] then error(result["errorMessage"]) end
self = nil self = nil
@ -37,13 +37,14 @@ Entry.delete = function(self:typeof(Entry))
end end
return { return {
new = function(tableId:string, entryData:Types.EntryData, accessToken:string):typeof(Entry) new = function(tableId:string, entryData:Types.EntryData, accessToken:string, options: Types.ClientOptions):typeof(Entry)
if not tableId or not entryData or not entryData["_id"] or not accessToken then error("[MarcSync: Entry] Tried creating invalid Entry Object.") end if not tableId or not entryData or not entryData["_id"] or not accessToken then error("[MarcSync: Entry] Tried creating invalid Entry Object.") end
local self = {} local self = {}
self._tableId = tableId self._tableId = tableId
self._entryData = entryData self._entryData = entryData
self._objectId = entryData["_id"] self._objectId = entryData["_id"]
self._accessToken = accessToken self._accessToken = accessToken
self._options = options
self = setmetatable(self, { self = setmetatable(self, {
__index = Entry __index = Entry

View File

@ -4,4 +4,8 @@ export type EntryData = {
[string]: any [string]: any
} }
export type ClientOptions = {
retryCount: number
}
return types return types

View File

@ -3,8 +3,9 @@ local CollectionError = require(script.Parent.Errors.Collection)
local EntryError = require(script.Parent.Errors.Entry) local EntryError = require(script.Parent.Errors.Entry)
local HttpService = game:GetService("HttpService") local HttpService = game:GetService("HttpService")
local Types = require(script.Parent.Types)
function errorHandler(type: string, resultBody: any, resultObject: {}) function errorHandler(callInformation: {}, resultBody: any, resultObject: {}, retryCount: number)
local Error; local Error;
if typeof(resultBody) == typeof({}) and resultBody["message"] then if typeof(resultBody) == typeof({}) and resultBody["message"] then
Error = resultBody["message"] Error = resultBody["message"]
@ -14,30 +15,39 @@ function errorHandler(type: string, resultBody: any, resultObject: {})
Error = "An Unexpected Error occoured." Error = "An Unexpected Error occoured."
end end
if type == "collection" then local statusCode = resultObject["StatusCode"]
if resultObject["StatusCode"] == 401 then if callInformation.type == "collection" then
if statusCode == 401 then
Error = AuthorizationError.InvalidAccessToken("InvalidAccessToken") Error = AuthorizationError.InvalidAccessToken("InvalidAccessToken")
elseif resultObject["StatusCode"] == 404 then elseif statusCode == 404 then
Error = CollectionError.CollectionNotFound("CollectionNotFound") Error = CollectionError.CollectionNotFound("CollectionNotFound")
elseif resultObject["StatusCode"] == 400 then elseif statusCode == 400 then
Error = CollectionError.CollectionAlreadyExists("CollectionAlreadyExists") Error = CollectionError.CollectionAlreadyExists("CollectionAlreadyExists")
end end
elseif type == "entry" then elseif callInformation.type == "entry" then
if resultObject["StatusCode"] == 401 then if statusCode == 401 then
Error = AuthorizationError.InvalidAccessToken("InvalidAccessToken") Error = AuthorizationError.InvalidAccessToken("InvalidAccessToken")
elseif resultObject["StatusCode"] == 404 then elseif statusCode == 404 then
Error = CollectionError.CollectionNotFound("CollectionNotFound") Error = CollectionError.CollectionNotFound("CollectionNotFound")
elseif resultObject["StatusCode"] == 400 then elseif statusCode == 400 then
Error = EntryError.InvalidEntryData("InvalidEntryData") Error = EntryError.InvalidEntryData("InvalidEntryData")
end end
end end
if(statusCode ~= 400 and statusCode ~= 401) then
if retryCount > 0 then
warn("[MarcSync HTTPRequest Handler] MarcSync HTTP Request failed with error: "..Error.." and status code: "..statusCode..". Retrying Request. ("..retryCount..") retries left")
task.wait(3)
return require(script.Parent.Utils).makeHTTPRequest(callInformation.type, callInformation.method, callInformation.url, callInformation.body, callInformation.authorization, {retryCount = retryCount - 1})
end
end
return {["success"] = false, ["errorMessage"] = Error} return {["success"] = false, ["errorMessage"] = Error}
end end
local utils = {} local utils = {}
function utils.makeHTTPRequest(type: string, method: string, url: string, body: {}, authorization: string):{["success"]: boolean, ["message"]: string} function utils.makeHTTPRequest(type: string, method: string, url: string, body: {}, authorization: string, options: Types.ClientOptions):{["success"]: boolean, ["message"]: string}
local resultObj; local resultObj;
local resultBody; local resultBody;
local success = pcall(function() local success = pcall(function()
@ -54,7 +64,13 @@ function utils.makeHTTPRequest(type: string, method: string, url: string, body:
if resultBody["warning"] then warn('[MarcSync HTTPRequest Handler] MarcSync HTTP Request returned warning for URL "'..url..'" with body: "'..HttpService:JSONEncode(body)..'": '..resultBody["warning"]) end if resultBody["warning"] then warn('[MarcSync HTTPRequest Handler] MarcSync HTTP Request returned warning for URL "'..url..'" with body: "'..HttpService:JSONEncode(body)..'": '..resultBody["warning"]) end
return resultBody return resultBody
end end
return errorHandler(type, resultBody, resultObj) return errorHandler({
type = type,
method = method,
url = url,
body = body,
authorization = authorization
}, resultBody, resultObj, options.retryCount)
end end
return utils return utils

View File

@ -18,6 +18,24 @@ METHODS
</div> </div>
## CONSTRUCTOR
### .new(`accessToken:` [string](https://www.lua.org/pil/2.4.html), `options:` [ClientOptions?](../types/ClientOptions))
Creates a new MarcSync client.
:::info Info
When creating a new client, you must provide an `accessToken`. This argument can either be the key of the "tokens" table in the client's Class or the actual token itself. If you provide the key, the client will automatically try to fetch the token from the "tokens" table. If you provide the token itself, the client will use that token instead. If no token is found by the key provided, it will use the key as the token itself.
:::
:::info Info
The `options` argument is optional and can be used to specify the options of the client. If you do not provide any options, the client will use the default options. The default options are as follows:
```lua
{
retryCount = 3
}
```
:::
## METHODS ## METHODS
### :getVersion(`clientId:` [number?](https://www.lua.org/pil/2.3.html)) ### :getVersion(`clientId:` [number?](https://www.lua.org/pil/2.3.html))

View File

@ -0,0 +1,11 @@
# ClientOptions
ClientOptions is a type which is used to specify the options of a client. It is used in the [MarcSyncClient](../classes/MarcSyncClient) class when creating a new client.
## Type Definition
```typescript
type ClientOptions = {
retryCount: number
}
```