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 MarcSyncClient = {}
local Types = require(script.Parent.Types)
MarcSyncClient.getVersion = function(self:typeof(MarcSyncClient), clientId: number?):string
self:_checkInstallation()
local url = ""
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"]
end
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 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
result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken)
result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken, self._options)
return result
end
MarcSyncClient.fetchCollection = function(self:typeof(MarcSyncClient), collectionName: string):typeof(require(script.Parent.Objects.Collection).new())
self:_checkInstallation()
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
result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken)
result = require(script.Parent.Objects.Collection).new(collectionName, self._accessToken, self._options)
return result
end
@ -40,15 +42,16 @@ MarcSyncClient.getCollection = function(self:typeof(MarcSyncClient), collectionN
if typeof(self) ~= "table" then error("Please use : instead of .") end
self:_checkInstallation()
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
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 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 = {}
self._accessToken = accessToken
self._options = options or {retryCount = 3}
self._checkInstallation = function()
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

View File

@ -7,11 +7,11 @@ local Collection = {}
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
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
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
error(result["errorMessage"])
end
@ -21,7 +21,7 @@ end
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
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
return result["modifiedEntries"]
@ -30,11 +30,11 @@ end
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 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
local _result = {}
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
result = _result
else
@ -46,7 +46,7 @@ end
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
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
return result["deletedEntries"]
@ -54,16 +54,17 @@ end
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
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
self = nil
end
return {
new = function(collectionName: string, accessToken: string):typeof(Collection)
new = function(collectionName: string, accessToken: string, options: Types.ClientOptions):typeof(Collection)
local self = {}
self._collectionName = collectionName
self._accessToken = accessToken
self._options = options
self = setmetatable(self, {
__index = Collection

View File

@ -14,7 +14,7 @@ Entry.getValues = function(self:typeof(Entry)):Types.EntryData
end
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
for i,v in pairs(data) do
@ -29,7 +29,7 @@ end
Entry.delete = function(self:typeof(Entry))
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
self = nil
@ -37,13 +37,14 @@ Entry.delete = function(self:typeof(Entry))
end
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
local self = {}
self._tableId = tableId
self._entryData = entryData
self._objectId = entryData["_id"]
self._accessToken = accessToken
self._options = options
self = setmetatable(self, {
__index = Entry

View File

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

View File

@ -3,8 +3,9 @@ local CollectionError = require(script.Parent.Errors.Collection)
local EntryError = require(script.Parent.Errors.Entry)
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;
if typeof(resultBody) == typeof({}) and resultBody["message"] then
Error = resultBody["message"]
@ -14,30 +15,39 @@ function errorHandler(type: string, resultBody: any, resultObject: {})
Error = "An Unexpected Error occoured."
end
if type == "collection" then
if resultObject["StatusCode"] == 401 then
local statusCode = resultObject["StatusCode"]
if callInformation.type == "collection" then
if statusCode == 401 then
Error = AuthorizationError.InvalidAccessToken("InvalidAccessToken")
elseif resultObject["StatusCode"] == 404 then
elseif statusCode == 404 then
Error = CollectionError.CollectionNotFound("CollectionNotFound")
elseif resultObject["StatusCode"] == 400 then
elseif statusCode == 400 then
Error = CollectionError.CollectionAlreadyExists("CollectionAlreadyExists")
end
elseif type == "entry" then
if resultObject["StatusCode"] == 401 then
elseif callInformation.type == "entry" then
if statusCode == 401 then
Error = AuthorizationError.InvalidAccessToken("InvalidAccessToken")
elseif resultObject["StatusCode"] == 404 then
elseif statusCode == 404 then
Error = CollectionError.CollectionNotFound("CollectionNotFound")
elseif resultObject["StatusCode"] == 400 then
elseif statusCode == 400 then
Error = EntryError.InvalidEntryData("InvalidEntryData")
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}
end
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 resultBody;
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
return resultBody
end
return errorHandler(type, resultBody, resultObj)
return errorHandler({
type = type,
method = method,
url = url,
body = body,
authorization = authorization
}, resultBody, resultObj, options.retryCount)
end
return utils

View File

@ -18,6 +18,24 @@ METHODS
</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
### :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
}
```