First commit

This commit is contained in:
Marcel Lorbeer 2023-06-14 14:34:30 +02:00
parent 1ed365db7b
commit 3fca233c3e
10 changed files with 474 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Project place file
/client.rbxlx
# Roblox Studio lock files
/*.rbxlx.lock
/*.rbxl.lock

7
aftman.toml Normal file
View File

@ -0,0 +1,7 @@
# This file lists tools managed by Aftman, a cross-platform toolchain manager.
# For more information, see https://github.com/LPGhatguy/aftman
# To add a new tool, add an entry to this table.
[tools]
rojo = "rojo-rbx/rojo@7.3.0"
# rojo = "rojo-rbx/rojo@6.2.0"

12
default.project.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "client",
"tree": {
"$className": "DataModel",
"ServerScriptService": {
"MarcSync": {
"$path": "src/MarcSync"
}
}
}
}

View File

@ -0,0 +1,211 @@
local tokens = {
["exampleToken"] = ""
}
-- DO NOT EDIT THE FOLLOWING LINES BELOW, UNLESS YOU KNOW WHAT YOU ARE DOING!
local Utils = require(script.Parent.Utils)
type Function = () -> ()
type DefaultResultType = (success: boolean, errorMessage: string) -> ()
type DefaultResult = {
["onResult"]: {
["Connect"]:DefaultResultType
}
}
local HttpService = game:GetService("HttpService")
local reqQueue = {}
coroutine.wrap(function()
while wait(5) do
for i,v in pairs(reqQueue) do
local result;
local success, Error = pcall(function() result = v.method(v.arguments) end)
if not success and Error == "Number of requests exceeded limit" then
break
else
v.event:__Fire(result)
table.remove(reqQueue, i)
end
end
end
end)
function signal()
local RBXSignal = {}
RBXSignal._bindableEvent = Instance.new("BindableEvent")
function RBXSignal:_Fire(...)
RBXSignal._bindableEvent:Fire(...)
end
function RBXSignal:Connect(handler: ({success: boolean, result: {}}) -> ({success: boolean, result: {}}))
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not (type(handler) == "function") then
error(("connect(%s)"):format(typeof(handler)), 2)
end
RBXSignal._bindableEvent.Event:Connect(function(...)
handler(...)
end)
end
return RBXSignal
end
local marcsync = {}
marcsync.__index = marcsync
marcsync.request = {}
function marcsync.new(token: string)
if not token then warn("Token not provided while creating a new MarcSync Object.") end
if not tokens[token] then warn("Token provided for creating a new MarcSync Object not Found in Token Table, using it as token instead.") else token = tokens[token] end
local self = setmetatable({}, marcsync)
self._token = token
self.request._parent = self
self.request.version._parent = self.request
self.request.database._parent = self.request
self.request.collection._parent = self.request
self.utils._parent = self
return self
end
function marcsync:_checkInstallation()
if not self then error("Please Setup MarcSync before using MarcSync.") end
if not self._token then error("[MarcSync] Please set a Token before using MarcSync.") end
--print(HttpService.HttpEnabled)
--if not HttpService.HttpEnabled then error("Please Enable HTTPService in order to use MarcSync.") end
end
function marcsync._errorHandler(RBXSignal: {}, Error: string)
spawn(function()
RBXSignal:_Fire({
["success"] = false,
["errorMessage"] = Error
})
end)
return {["onResult"] = RBXSignal}
end
function marcsync:_requestHandler(result: {}, Error: string):{["onResult"]: {}}
if result == nil then return self._errorHandler(signal(), Error) end
local errorResult;
local signalevent;
if #result.Body == 0 and not result.Success then
errorResult = "HTTP "..result.StatusCode.." ("..result.StatusMessage..")" result = false
elseif not pcall(function() HttpService:JSONDecode(result.Body) end) then
error("Unexpected MarcSync Result.")
else
result = result.Body
signalevent = {["onResult"]=signal()}
end
spawn(function()
if not result then return end
signalevent.onResult:_Fire(result)
end)
return signalevent or self._errorHandler(signal(), errorResult)
end
marcsync.request.version = {}
function marcsync.request.version:get(clientId: number?):{["success"]:boolean,["version"]:string,["update_server"]:string}
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._parent then error("[MarcSync] Please set a Token before using MarcSync.") end
self._parent._parent:_checkInstallation()
local url = ""
if clientId then url = "/"..clientId end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("GET", "https://api.marcsync.dev/v0/utils/version"..url)).onResult:Connect(function(any)
result = any
end)
repeat
wait()
until result ~= nil
return result
end
marcsync.request.collection = {}
function marcsync.request.collection:create(collectionName: string):typeof(require(script.Parent.Objects.Collection))
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._parent then error("[MarcSync] Please set a Token before using MarcSync.") end
self._parent._parent:_checkInstallation()
if not collectionName then error("No CollectionName Provided") end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("POST", "https://api.marcsync.dev/v0/collection/"..collectionName, {}, self._parent._parent._token)).onResult:Connect(function(any)
if not any["success"] then result = false return end
result = require(script.Parent.Objects.Collection)._new(collectionName, self._parent._parent._token)
end)
repeat
wait()
until result ~= nil
return result
end
function marcsync.request.collection:fetch(collectionName: string):typeof(require(script.Parent.Objects.Collection))
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._parent then error("[MarcSync] Please set a Token before using MarcSync.") end
self._parent._parent:_checkInstallation()
if not collectionName then error("No CollectionName Provided") end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("GET", "https://api.marcsync.dev/v0/collection/"..collectionName, {}, self._parent._parent._token)).onResult:Connect(function(any)
if not any["success"] then result = false return end
result = require(script.Parent.Objects.Collection)._new(collectionName, self._parent._parent._token)
end)
repeat
wait()
until result ~= nil
return result
end
function marcsync.request.collection:get(collectionName: string):typeof(require(script.Parent.Objects.Collection))
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._parent then error("[MarcSync] Please set a Token before using MarcSync.") end
self._parent._parent:_checkInstallation()
if not collectionName then error("No CollectionName Provided") end
return require(script.Parent.Objects.Collection)._new(collectionName, self._parent._parent._token)
end
marcsync.request.database = {}
function marcsync.request.database:fetch(databaseId: number):DefaultResult
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._parent then error("[MarcSync] Please set a Token before using MarcSync.") end
self._parent._parent:_checkInstallation()
if not databaseId then error("No DatabaseId Provided") end
local result;
local success, Error = pcall(function() result = HttpService:RequestAsync({Url = "https://api.marcthedev.it/marcsync/v0/database/"..databaseId, Headers = {["Authorization"]=self._parent._parent._token}}) end)
return self._parent._parent:_requestHandler(result, Error)
end
function marcsync.request.database:delete(databaseId: number):DefaultResult
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._parent then error("[MarcSync] Please set a Token before using MarcSync.") end
self._parent._parent:_checkInstallation()
if not databaseId then error("No DatabaseId Provided") end
local result;
local success, Error = pcall(function() result = HttpService:RequestAsync({Url = "https://api.marcthedev.it/marcsync/v0/database/"..databaseId, Headers = {["Authorization"]=self._parent._parent._token}}) end)
return self._parent._parent:_requestHandler(result, Error)
end
marcsync.utils = {}
function marcsync.utils.safeRequest(method: Function, arguments: {})
--_checkInstallation()
--[[
spawn(function()
local result;
local success, Error = pcall(function() result = method(arguments) end)
if not success and Error == "Number of requests exceeded limit" then
reqQueue[#reqQueue] = {["method"] = method, ["arguments"] = arguments, ["event"] = RBXSignal}
elseif success then
RBXSignal:__Fire(result)
end
end)
return { ["onResult"] = RBXSignal}
--]]
end
function marcsync.utils.bulkRequest(methods: {}, safeReq: boolean?):{}
--_checkInstallation()
local returns = {}
for i,method in pairs(methods) do if safeReq then marcsync.safeRequest(method[1], method[2]).onResult:Connect(function(result) returns[i] = result end) else returns[i] = method[1](method[2]) end end
while #methods ~= #returns do
wait()
end
return returns
end
return marcsync

View File

@ -0,0 +1,109 @@
local Utils = require(script.Parent.Parent.Utils)
local Entry = require(script.Parent.Entry)
local collection = {}
collection.__index = collection
local filterSheme = {
["values"]=typeof({ ["key"]=... }),
["startsWith"]=typeof({ "key" }),
["ignoreCases"]=typeof({ "key" })
}
function collection:insert(data:{key:any}):typeof(Entry)?
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("POST", "https://api.marcsync.dev/v0/entries/"..self._collectionName, {["data"]=data}, self._token)).onResult:Connect(function(any)
if any["success"] and any["objectId"] then
data["_id"] = any["objectId"]
result = require(script.Parent.Entry)._new(self._collectionName, data, self._token)
return
end
result = any
end)
repeat
wait()
until result ~= nil
return result
end
function collection:update(filters:typeof(filterSheme),data:{key:any}):{message:string?,errorMessage:string?,success:boolean,modifiedEntries:number?}
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = nil;
if not filters.values then error("[MarcSync: Collection] Invalid arguments given for collection:select(). Expected filters.values, got nil") end
filters.values["_startsWith"] = filters.startsWith or {}
filters.values["_ignoreCases"] = filters.ignoreCases or {}
Utils.handleResponse(Utils.makeHTTPRequest("PUT", "https://api.marcsync.dev/v1/entries/"..self._collectionName, {["filters"]=filters.values,["data"]=data}, self._token)).onResult:Connect(function(any)
result = any
end)
repeat
wait()
until result ~= nil
return result
end
function collection:select(filters:typeof(filterSheme),limit:number):{entries:{a:typeof(Entry)?,b:typeof(Entry)?,c:typeof(Entry)?}?,success:boolean,message:string?,errorMessage:string?}
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = nil;
if not filters.values then error("[MarcSync: Collection] Invalid arguments given for collection:select(). Expected filters.values, got nil") end
filters.values["_startsWith"] = filters.startsWith or {}
filters.values["_ignoreCases"] = filters.ignoreCases or {}
Utils.handleResponse(Utils.makeHTTPRequest("PATCH", "https://api.marcsync.dev/v1/entries/"..self._collectionName.."?methodOverwrite=GET", {["filters"]=filters.values, ["limit"]=limit}, self._token)).onResult:Connect(function(any)
if any["success"] and any["entries"] then
local _result = {["entries"]={},["success"]=true}
for index,entry in pairs(any["entries"]) do
_result["entries"][index] = require(script.Parent.Entry)._new(self._collectionName, entry, self._token)
end
result = _result
return
end
result = any
end)
repeat
wait()
until result ~= nil
return result
end
function collection:delete(filters:typeof(filterSheme)):{message:string?,errorMessage:string?,success:boolean,deletedEntries:number?}
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = nil;
if not filters.values then error("[MarcSync: Collection] Invalid arguments given for collection:select(). Expected filters.values, got nil") end
filters.values["_startsWith"] = filters.startsWith or {}
filters.values["_ignoreCases"] = filters.ignoreCases or {}
Utils.handleResponse(Utils.makeHTTPRequest("DELETE", "https://api.marcsync.dev/v1/entries/"..self._collectionName, {["filters"]=filters.values}, self._token)).onResult:Connect(function(any)
result = any
end)
repeat
wait()
until result ~= nil
return result
end
function collection:drop():{success:boolean,message:string?,errorMessage:string?}
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not self._collectionName then error("[MarcSync: Collection] Invalid Object created or trying to access an destroied object.") end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("DELETE", "https://api.marcsync.dev/v0/collection/"..self._collectionName, {}, self._token)).onResult:Connect(function(any)
result = any
end)
repeat
wait()
until result ~= nil
return result
end
collection._collectionName = nil
function collection._new(_collectionName: string, _token: string):typeof(collection)
local self = setmetatable({}, collection)
self._collectionName = _collectionName
self._token = _token
return self
end
return collection

View File

@ -0,0 +1,62 @@
local Utils = require(script.Parent.Parent.Utils)
local entry = {}
entry.__index = entry
function entry:getValue(value:string):any
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not value then return nil end
return self._values[value]
end
function entry:getValues():{}
if typeof(self) ~= "table" then error("Please use : instead of .") end
return self._values
end
function entry:update(newData:{key:any}):{success:boolean,errorMessage:string?,message:string?,modifiedEntries:IntValue?}
if typeof(self) ~= "table" then error("Please use : instead of .") end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("PUT", "https://api.marcsync.dev/v0/entries/"..self._tableId, {["filters"]={["_id"]=self._objectId},["data"]=newData}, self._token)).onResult:Connect(function(any)
if any["success"] and any["modifiedEntries"] and any["modifiedEntries"] > 0 then
for i,v in pairs(newData) do
self._values[i] = v
end
end
result = any
end)
repeat
wait()
until result ~= nil
return result
end
function entry:delete():{success:boolean,errorMessage:string?,message:string?,deletedEntries:IntValue?}
if typeof(self) ~= "table" then error("Please use : instead of .") end
local result = nil;
Utils.handleResponse(Utils.makeHTTPRequest("DELETE", "https://api.marcsync.dev/v0/entries/"..self._tableId, {["filters"]={["_id"]=self._objectId}}, self._token)).onResult:Connect(function(any)
if any["success"] and any["deletedEntries"] then
if any["deletedEntries"] < 1 then result = false return end
spawn(function()
self = nil
end)
end
result = any
end)
repeat
wait()
until result ~= nil
return result
end
function entry._new(_tableId:string, _values:{}, _token:string):typeof(entry)
if not _tableId or not _values or not _values["_id"] then error("[MarcSync: Entry] Tried creating invalid Entry Object.") end
local self = setmetatable({}, entry)
self._tableId = _tableId
self._values = _values
self._objectId = _values["_id"]
self._token = _token
return self
end
return entry

View File

@ -0,0 +1 @@
return require(script.Parent.Parent.Parent.Objects.Collection)

View File

@ -0,0 +1 @@
return require(script.Parent.Parent.Parent.Objects.Entry)

View File

@ -0,0 +1 @@
return require(script.Parent.Parent)

64
src/MarcSync/Utils.lua Normal file
View File

@ -0,0 +1,64 @@
local HttpService = game:GetService("HttpService")
function errorHandler(RBXSignal: {}, result: any)
local Error;
if typeof(result) == typeof({}) and result["message"] then
Error = result["message"]
elseif typeof(result) == typeof("") then
Error = result
else
Error = "An Unexpected Error occoured."
end
spawn(function()
RBXSignal:_Fire({
["success"] = false,
["errorMessage"] = Error
})
end)
return {["onResult"] = RBXSignal}
end
local utils = {}
function utils._signal()
local RBXSignal = {}
RBXSignal._bindableEvent = Instance.new("BindableEvent")
function RBXSignal:_Fire(...)
RBXSignal._bindableEvent:Fire(...)
end
function RBXSignal:Connect(handler: ({success: boolean, result: {}}) -> ({success: boolean, result: {}}))
if typeof(self) ~= "table" then error("Please use : instead of .") end
if not (type(handler) == "function") then
error(("connect(%s)"):format(typeof(handler)), 2)
end
RBXSignal._bindableEvent.Event:Connect(function(...)
handler(...)
end)
end
return RBXSignal
end
function utils.handleResponse(result: any, error: boolean, signal: RBXScriptSignal?)
if error then return result end
spawn(function()
signal:_Fire(result)
end)
return {["onResult"] = signal}
end
function utils.makeHTTPRequest(method: string, url: string, body: {}, authorization: string):{["success"]: boolean, ["message"]: string}
local result;
local success = pcall(function()
if body then body = HttpService:JSONEncode(body) end
if (method == "GET" or method == "HEAD") then
result = HttpService:JSONDecode(HttpService:RequestAsync({Method=method, Url=url, Headers={["Authorization"]=authorization,["Content-Type"]="application/json"}})["Body"])
else
result = HttpService:JSONDecode(HttpService:RequestAsync({Method=method, Url=url, Headers={["Authorization"]=authorization,["Content-Type"]="application/json"}, Body=body})["Body"])
end
end)
if success and result and result["success"] then
if result["warning"] then warn('[MarcSync HTTPRequest Handler] MarcSync HTTP Request returned warning for URL "'..url..'" with body: "'..HttpService:JSONEncode(body)..'": '..result["warning"]) end
return result, false, utils._signal()
end
return errorHandler(utils._signal(), result), true
end
return utils