@neonwilderness/ecjsonstorage 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +87 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
- package/src/index.ts +296 -0
- package/test/api.test.mts +136 -0
package/.env.example
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
All notable changes to this project will be documented in this file.
|
|
3
|
+
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.0.0] - 2026-09-29
|
|
8
|
+
- Initial release
|
|
9
|
+
|
|
10
|
+
## [1.0.1] - 2026-09-30
|
|
11
|
+
- Fix package.json main,exports,types
|
|
12
|
+
- Enhance readme
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NeonWilderness
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# About ECJsonStorage
|
|
2
|
+
ECJsonStorage is a TS/JS wrapper to the [ExtendsClass JSON Storage API](https://extendsclass.com/json-storage.html) offering of Cyril Bois.
|
|
3
|
+
|
|
4
|
+
For API usage, you need a (free) API-Key for which you can apply [here](https://extendsclass.com/create-account-form).
|
|
5
|
+
|
|
6
|
+
ECJsonStorage enables you to create, update, patch and delete Bins, which store JSON data. There is a usage limit of
|
|
7
|
+
- 10.000 calls per month
|
|
8
|
+
- Bin size limit of 100kb
|
|
9
|
+
- total account limit of 10mb.
|
|
10
|
+
|
|
11
|
+
# Installation
|
|
12
|
+
|
|
13
|
+
- `npm i ecjsonstorage -S` to install the module, which is then both available as CommonJs and ESM version.
|
|
14
|
+
|
|
15
|
+
- Create an .env file in your project directory root and add the following keys:
|
|
16
|
+
|
|
17
|
+
`JSONAPI=your ExtendsClass Api-key`
|
|
18
|
+
|
|
19
|
+
`JSONSEC=your self defined Security-key (recommended)`
|
|
20
|
+
|
|
21
|
+
The Security-key is used to protect access, modification and deletion of a bin. If a Security-key is passed during create, it will also be required to update, patch and delete this bin.
|
|
22
|
+
|
|
23
|
+
The Security-key is also required to request private bins (i.e. bins that were created with `keepPrivate=true`), but not required for public bins.
|
|
24
|
+
|
|
25
|
+
# Usage
|
|
26
|
+
|
|
27
|
+
## Constructor
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
import { ECJsonStorage } from 'ecjsonstorage';
|
|
31
|
+
import { config } from 'dotenv-safe';
|
|
32
|
+
config();
|
|
33
|
+
|
|
34
|
+
const json = new ECJsonStorage(process.env.JSONAPI, process.env.JSONSEC);
|
|
35
|
+
...
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Create a bin
|
|
39
|
+
|
|
40
|
+
`createBin(payload: object, keepPrivate: boolean)`
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
const json = new ECJsonStorage(process.env.JSONAPI, process.env.JSONSEC);
|
|
44
|
+
const payload = { name: 'Neon', profession: 'IT-Consultant' };
|
|
45
|
+
const res = await json.createBin(payload, true);
|
|
46
|
+
console.log(res);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Further Documentation/Usage details will follow swiftly; for now you may wanna consult the [TS types file](dist/index.d.cts).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var a=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var y=(i,t)=>{for(var e in t)a(i,e,{get:t[e],enumerable:!0})},l=(i,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of u(t))!h.call(i,s)&&s!==e&&a(i,s,{get:()=>t[s],enumerable:!(r=p(t,s))||r.enumerable});return i};var d=i=>l(a({},"__esModule",{value:!0}),i);var g={};y(g,{ECJsonStorage:()=>o});module.exports=d(g);var o=class{url;apiKey;securityKey;monthlyLimit=1e4;constructor(t,e,r){if(this.url=r||"https://json.extendsclass.com/bin",this.apiKey=t||"",this.securityKey=e||"",!this.url||!this.apiKey)throw new Error("Invalid constructor params!");for(;this.url.endsWith("/");)this.url=this.url.slice(0,-1)}validateResponse=async t=>{if(!t.ok)throw new Error(`HTTP error! Status: ${t.status}`);if((t.headers.get("content-type")||"").startsWith("text/html"))throw new Error(`Expected JSON but server returned HTML: "${(await t.text()).slice(0,100)}..."`)};getBins=async()=>{try{let t=await fetch(`${this.url}s`,{method:"GET",headers:{"Api-key":this.apiKey,Accept:"application/json"}});await this.validateResponse(t);let e=Number(t.headers.get("x-counter")),r=await t.json();return{status:"ok",callsDone:e,callsRemaining:this.monthlyLimit-e,bins:r}}catch(t){return console.error("getBins error:",t),{status:"error",statusText:t}}};getStats=async()=>{try{let t=await this.getBins();if(t.status==="error")throw new Error(`HTTP error! Status: ${t.statusText}`);return delete t.bins,t}catch(t){return console.error("getStats error:",t),{status:"error",statusText:t}}};getBin=async t=>{try{if(!t)throw new Error("Bin ID missing!");let e=await fetch(`${this.url}/${t}`,{method:"GET",headers:{"Security-key":this.securityKey,Accept:"application/json"}});return await this.validateResponse(e),{status:"ok",data:await e.json()}}catch(e){return console.error("getBin error:",e),{status:"error",statusText:e}}};createBin=async(t,e)=>{let r=new Headers({"Api-key":this.apiKey,"Security-key":this.securityKey,"Content-Type":"application/json",Accept:"application/json"});r.append("Private",e.toString());try{let s=await fetch(this.url,{method:"POST",headers:r,body:JSON.stringify(t)});await this.validateResponse(s);let{uri:c,bin:n}=await s.json();return n||(n=c.split("/").pop()),{status:"ok",uri:c,bin:n}}catch(s){return console.error("createBin error:",s),{status:"error",statusText:s}}};updateBin=async(t,e)=>{try{let r=await fetch(`${this.url}/${e}`,{method:"PUT",headers:{"Security-key":this.securityKey,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(t)});await this.validateResponse(r);let{data:s}=await r.json();return{status:"ok",data:s}}catch(r){return console.error("updateBin error:",r),{status:"error",statusText:r}}};patchBin=async(t,e)=>{try{let r=await fetch(`${this.url}/${e}`,{method:"PATCH",headers:{"Security-key":this.securityKey,"Content-Type":"application/merge-patch+json",Accept:"application/json"},body:JSON.stringify(t)});await this.validateResponse(r);let{data:s}=await r.json();return{status:"ok",data:s}}catch(r){return console.error("patchBin error:",r),{status:"error",statusText:r}}};deleteBin=async t=>{try{let e=await fetch(`${this.url}/${t}`,{method:"DELETE",headers:{"Security-key":this.securityKey,"Content-Type":"application/json",Accept:"application/json"}});await this.validateResponse(e);let{status:r}=await e.json();if(r!==0)throw new Error(`Bin delete failed with error: ${r.message}`);return{status:"ok"}}catch(e){return console.error("deleteBin error:",e),{status:"error",statusText:e}}}};0&&(module.exports={ECJsonStorage});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\r\n * ESJsonStorage class is a wrapper to the ExtendsClass JSON-Storage API\r\n */\r\nexport type resGetBins = {\r\n status: 'ok';\r\n callsDone: number;\r\n callsRemaining: number;\r\n bins?: string[];\r\n};\r\nexport type resGetStats = Omit<resGetBins, 'bins'>;\r\n\r\nexport type resGetBin = {\r\n status: 'ok';\r\n data: object;\r\n};\r\n\r\nexport type resUpdateBin = {\r\n status: 'ok';\r\n data: string;\r\n};\r\nexport type resPatchBin = resUpdateBin;\r\n\r\nexport type resCreateBin = {\r\n status: 'ok';\r\n uri: string;\r\n bin: string;\r\n};\r\n\r\nexport type resDeleteBin = Omit<resGetBin, 'data'>;\r\n\r\nexport type resError = {\r\n status: 'error';\r\n statusText: string;\r\n};\r\n\r\nexport class ECJsonStorage {\r\n url: string;\r\n apiKey: string;\r\n securityKey: string;\r\n monthlyLimit: number = 10000;\r\n\r\n constructor(apiKey: string, securityKey?: string, url?: string) {\r\n this.url = url || 'https://json.extendsclass.com/bin';\r\n this.apiKey = apiKey || '';\r\n this.securityKey = securityKey || '';\r\n\r\n if (!this.url || !this.apiKey) throw new Error('Invalid constructor params!');\r\n while (this.url.endsWith('/')) {\r\n this.url = this.url.slice(0, -1);\r\n }\r\n }\r\n\r\n /**\r\n * Utility function to validate the response after the class function call\r\n * @param response\r\n */\r\n validateResponse = async (response: Response) => {\r\n if (!response.ok) {\r\n throw new Error(`HTTP error! Status: ${response.status}`);\r\n }\r\n\r\n const contentType = response.headers.get('content-type') || '';\r\n if (contentType.startsWith('text/html')) {\r\n throw new Error(`Expected JSON but server returned HTML: \"${(await response.text()).slice(0, 100)}...\"`);\r\n }\r\n };\r\n\r\n /**\r\n * Get all bin IDs for a certain Api-key\r\n * @returns {object} <resGetBins | resError>\r\n */\r\n getBins = async (): Promise<resGetBins | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}s`, {\r\n method: 'GET',\r\n headers: {\r\n 'Api-key': this.apiKey,\r\n Accept: 'application/json'\r\n }\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const counter = Number(response.headers.get('x-counter'));\r\n const data = await response.json();\r\n return {\r\n status: 'ok',\r\n callsDone: counter,\r\n callsRemaining: this.monthlyLimit - counter,\r\n bins: data\r\n };\r\n } catch (error) {\r\n console.error('getBins error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Syntactic sugar function for getBins to only return limit counter\r\n * @returns {object} <resGetStats | resError>\r\n */\r\n getStats = async (): Promise<resGetStats | resError> => {\r\n try {\r\n const res = await this.getBins();\r\n if (res.status === 'error') {\r\n throw new Error(`HTTP error! Status: ${(<resError>res).statusText}`);\r\n }\r\n\r\n delete (<resGetBins>res).bins;\r\n return <resGetStats>res;\r\n } catch (error) {\r\n console.error('getStats error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Reads the content of a certain bin ID\r\n * @param bin string Bin ID to read\r\n * @returns {object} <resGetBin | resError>\r\n */\r\n getBin = async (bin: string): Promise<resGetBin | resError> => {\r\n try {\r\n if (!bin) throw new Error('Bin ID missing!');\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'GET',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n Accept: 'application/json'\r\n }\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const data = await response.json();\r\n return {\r\n status: 'ok',\r\n data\r\n };\r\n } catch (error) {\r\n console.error('getBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Creates a new bin and saves the given JSON payload to it\r\n * @param payload {object} JSON object to create the bin with\r\n * @param keepPrivate {boolean} TRUE=bin shall be kept private\r\n * @returns {object} <resCreateBin | resError>\r\n */\r\n createBin = async (payload: object, keepPrivate: boolean): Promise<resCreateBin | resError> => {\r\n const headers = new Headers({\r\n 'Api-key': this.apiKey,\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json'\r\n });\r\n headers.append('Private', keepPrivate.toString());\r\n try {\r\n const response = await fetch(this.url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(payload)\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n let { uri, bin } = await response.json();\r\n if (!bin) bin = uri.split('/').pop();\r\n\r\n return {\r\n status: 'ok',\r\n uri,\r\n bin\r\n };\r\n } catch (error) {\r\n console.error('createBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Updates a certain bin with the given JSON object\r\n * @param payload {object} JSON object to update the bin with\r\n * @param bin string Bin ID to be updated\r\n * @returns {object} <resUpdateBin | resError>\r\n */\r\n updateBin = async (payload: object, bin: string): Promise<resUpdateBin | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'PUT',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json'\r\n },\r\n body: JSON.stringify(payload)\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const { data } = await response.json();\r\n return {\r\n status: 'ok',\r\n data\r\n };\r\n } catch (error) {\r\n console.error('updateBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Patches/inserts/changes a certain bin with the given JSON payload\r\n * ref: \"Partially update JSON\" at https://extendsclass.com/json-storage.html#apiDocumentation\r\n * @param payload {object} JSON object to patch the bin with\r\n * @param bin string Bin ID to be patched\r\n * @returns {object} <resPatchBin | resError>\r\n */\r\n patchBin = async (payload: object, bin: string): Promise<resPatchBin | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'PATCH',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/merge-patch+json',\r\n Accept: 'application/json'\r\n },\r\n body: JSON.stringify(payload)\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const { data } = await response.json();\r\n return {\r\n status: 'ok',\r\n data\r\n };\r\n } catch (error) {\r\n console.error('patchBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Deletes a certain bin\r\n * @param bin string Bin ID to be deleted\r\n * @returns {object} <resDeleteBin | resError>\r\n */\r\n deleteBin = async (bin: string): Promise<resDeleteBin | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'DELETE',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json'\r\n }\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const { status } = await response.json();\r\n if (status !== 0) throw new Error(`Bin delete failed with error: ${status.message}`);\r\n\r\n return {\r\n status: 'ok'\r\n };\r\n } catch (error) {\r\n console.error('deleteBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n}\r\n"],"mappings":"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","names":["index_exports","__export","ECJsonStorage","__toCommonJS","apiKey","securityKey","url","response","counter","data","error","res","bin","payload","keepPrivate","headers","uri","status"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESJsonStorage class is a wrapper to the ExtendsClass JSON-Storage API
|
|
3
|
+
*/
|
|
4
|
+
type resGetBins = {
|
|
5
|
+
status: 'ok';
|
|
6
|
+
callsDone: number;
|
|
7
|
+
callsRemaining: number;
|
|
8
|
+
bins?: string[];
|
|
9
|
+
};
|
|
10
|
+
type resGetStats = Omit<resGetBins, 'bins'>;
|
|
11
|
+
type resGetBin = {
|
|
12
|
+
status: 'ok';
|
|
13
|
+
data: object;
|
|
14
|
+
};
|
|
15
|
+
type resUpdateBin = {
|
|
16
|
+
status: 'ok';
|
|
17
|
+
data: string;
|
|
18
|
+
};
|
|
19
|
+
type resPatchBin = resUpdateBin;
|
|
20
|
+
type resCreateBin = {
|
|
21
|
+
status: 'ok';
|
|
22
|
+
uri: string;
|
|
23
|
+
bin: string;
|
|
24
|
+
};
|
|
25
|
+
type resDeleteBin = Omit<resGetBin, 'data'>;
|
|
26
|
+
type resError = {
|
|
27
|
+
status: 'error';
|
|
28
|
+
statusText: string;
|
|
29
|
+
};
|
|
30
|
+
declare class ECJsonStorage {
|
|
31
|
+
url: string;
|
|
32
|
+
apiKey: string;
|
|
33
|
+
securityKey: string;
|
|
34
|
+
monthlyLimit: number;
|
|
35
|
+
constructor(apiKey: string, securityKey?: string, url?: string);
|
|
36
|
+
/**
|
|
37
|
+
* Utility function to validate the response after the class function call
|
|
38
|
+
* @param response
|
|
39
|
+
*/
|
|
40
|
+
validateResponse: (response: Response) => Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Get all bin IDs for a certain Api-key
|
|
43
|
+
* @returns {object} <resGetBins | resError>
|
|
44
|
+
*/
|
|
45
|
+
getBins: () => Promise<resGetBins | resError>;
|
|
46
|
+
/**
|
|
47
|
+
* Syntactic sugar function for getBins to only return limit counter
|
|
48
|
+
* @returns {object} <resGetStats | resError>
|
|
49
|
+
*/
|
|
50
|
+
getStats: () => Promise<resGetStats | resError>;
|
|
51
|
+
/**
|
|
52
|
+
* Reads the content of a certain bin ID
|
|
53
|
+
* @param bin string Bin ID to read
|
|
54
|
+
* @returns {object} <resGetBin | resError>
|
|
55
|
+
*/
|
|
56
|
+
getBin: (bin: string) => Promise<resGetBin | resError>;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new bin and saves the given JSON payload to it
|
|
59
|
+
* @param payload {object} JSON object to create the bin with
|
|
60
|
+
* @param keepPrivate {boolean} TRUE=bin shall be kept private
|
|
61
|
+
* @returns {object} <resCreateBin | resError>
|
|
62
|
+
*/
|
|
63
|
+
createBin: (payload: object, keepPrivate: boolean) => Promise<resCreateBin | resError>;
|
|
64
|
+
/**
|
|
65
|
+
* Updates a certain bin with the given JSON object
|
|
66
|
+
* @param payload {object} JSON object to update the bin with
|
|
67
|
+
* @param bin string Bin ID to be updated
|
|
68
|
+
* @returns {object} <resUpdateBin | resError>
|
|
69
|
+
*/
|
|
70
|
+
updateBin: (payload: object, bin: string) => Promise<resUpdateBin | resError>;
|
|
71
|
+
/**
|
|
72
|
+
* Patches/inserts/changes a certain bin with the given JSON payload
|
|
73
|
+
* ref: "Partially update JSON" at https://extendsclass.com/json-storage.html#apiDocumentation
|
|
74
|
+
* @param payload {object} JSON object to patch the bin with
|
|
75
|
+
* @param bin string Bin ID to be patched
|
|
76
|
+
* @returns {object} <resPatchBin | resError>
|
|
77
|
+
*/
|
|
78
|
+
patchBin: (payload: object, bin: string) => Promise<resPatchBin | resError>;
|
|
79
|
+
/**
|
|
80
|
+
* Deletes a certain bin
|
|
81
|
+
* @param bin string Bin ID to be deleted
|
|
82
|
+
* @returns {object} <resDeleteBin | resError>
|
|
83
|
+
*/
|
|
84
|
+
deleteBin: (bin: string) => Promise<resDeleteBin | resError>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { ECJsonStorage, type resCreateBin, type resDeleteBin, type resError, type resGetBin, type resGetBins, type resGetStats, type resPatchBin, type resUpdateBin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var a=class{url;apiKey;securityKey;monthlyLimit=1e4;constructor(t,e,r){if(this.url=r||"https://json.extendsclass.com/bin",this.apiKey=t||"",this.securityKey=e||"",!this.url||!this.apiKey)throw new Error("Invalid constructor params!");for(;this.url.endsWith("/");)this.url=this.url.slice(0,-1)}validateResponse=async t=>{if(!t.ok)throw new Error(`HTTP error! Status: ${t.status}`);if((t.headers.get("content-type")||"").startsWith("text/html"))throw new Error(`Expected JSON but server returned HTML: "${(await t.text()).slice(0,100)}..."`)};getBins=async()=>{try{let t=await fetch(`${this.url}s`,{method:"GET",headers:{"Api-key":this.apiKey,Accept:"application/json"}});await this.validateResponse(t);let e=Number(t.headers.get("x-counter")),r=await t.json();return{status:"ok",callsDone:e,callsRemaining:this.monthlyLimit-e,bins:r}}catch(t){return console.error("getBins error:",t),{status:"error",statusText:t}}};getStats=async()=>{try{let t=await this.getBins();if(t.status==="error")throw new Error(`HTTP error! Status: ${t.statusText}`);return delete t.bins,t}catch(t){return console.error("getStats error:",t),{status:"error",statusText:t}}};getBin=async t=>{try{if(!t)throw new Error("Bin ID missing!");let e=await fetch(`${this.url}/${t}`,{method:"GET",headers:{"Security-key":this.securityKey,Accept:"application/json"}});return await this.validateResponse(e),{status:"ok",data:await e.json()}}catch(e){return console.error("getBin error:",e),{status:"error",statusText:e}}};createBin=async(t,e)=>{let r=new Headers({"Api-key":this.apiKey,"Security-key":this.securityKey,"Content-Type":"application/json",Accept:"application/json"});r.append("Private",e.toString());try{let s=await fetch(this.url,{method:"POST",headers:r,body:JSON.stringify(t)});await this.validateResponse(s);let{uri:n,bin:i}=await s.json();return i||(i=n.split("/").pop()),{status:"ok",uri:n,bin:i}}catch(s){return console.error("createBin error:",s),{status:"error",statusText:s}}};updateBin=async(t,e)=>{try{let r=await fetch(`${this.url}/${e}`,{method:"PUT",headers:{"Security-key":this.securityKey,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(t)});await this.validateResponse(r);let{data:s}=await r.json();return{status:"ok",data:s}}catch(r){return console.error("updateBin error:",r),{status:"error",statusText:r}}};patchBin=async(t,e)=>{try{let r=await fetch(`${this.url}/${e}`,{method:"PATCH",headers:{"Security-key":this.securityKey,"Content-Type":"application/merge-patch+json",Accept:"application/json"},body:JSON.stringify(t)});await this.validateResponse(r);let{data:s}=await r.json();return{status:"ok",data:s}}catch(r){return console.error("patchBin error:",r),{status:"error",statusText:r}}};deleteBin=async t=>{try{let e=await fetch(`${this.url}/${t}`,{method:"DELETE",headers:{"Security-key":this.securityKey,"Content-Type":"application/json",Accept:"application/json"}});await this.validateResponse(e);let{status:r}=await e.json();if(r!==0)throw new Error(`Bin delete failed with error: ${r.message}`);return{status:"ok"}}catch(e){return console.error("deleteBin error:",e),{status:"error",statusText:e}}}};export{a as ECJsonStorage};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\r\n * ESJsonStorage class is a wrapper to the ExtendsClass JSON-Storage API\r\n */\r\nexport type resGetBins = {\r\n status: 'ok';\r\n callsDone: number;\r\n callsRemaining: number;\r\n bins?: string[];\r\n};\r\nexport type resGetStats = Omit<resGetBins, 'bins'>;\r\n\r\nexport type resGetBin = {\r\n status: 'ok';\r\n data: object;\r\n};\r\n\r\nexport type resUpdateBin = {\r\n status: 'ok';\r\n data: string;\r\n};\r\nexport type resPatchBin = resUpdateBin;\r\n\r\nexport type resCreateBin = {\r\n status: 'ok';\r\n uri: string;\r\n bin: string;\r\n};\r\n\r\nexport type resDeleteBin = Omit<resGetBin, 'data'>;\r\n\r\nexport type resError = {\r\n status: 'error';\r\n statusText: string;\r\n};\r\n\r\nexport class ECJsonStorage {\r\n url: string;\r\n apiKey: string;\r\n securityKey: string;\r\n monthlyLimit: number = 10000;\r\n\r\n constructor(apiKey: string, securityKey?: string, url?: string) {\r\n this.url = url || 'https://json.extendsclass.com/bin';\r\n this.apiKey = apiKey || '';\r\n this.securityKey = securityKey || '';\r\n\r\n if (!this.url || !this.apiKey) throw new Error('Invalid constructor params!');\r\n while (this.url.endsWith('/')) {\r\n this.url = this.url.slice(0, -1);\r\n }\r\n }\r\n\r\n /**\r\n * Utility function to validate the response after the class function call\r\n * @param response\r\n */\r\n validateResponse = async (response: Response) => {\r\n if (!response.ok) {\r\n throw new Error(`HTTP error! Status: ${response.status}`);\r\n }\r\n\r\n const contentType = response.headers.get('content-type') || '';\r\n if (contentType.startsWith('text/html')) {\r\n throw new Error(`Expected JSON but server returned HTML: \"${(await response.text()).slice(0, 100)}...\"`);\r\n }\r\n };\r\n\r\n /**\r\n * Get all bin IDs for a certain Api-key\r\n * @returns {object} <resGetBins | resError>\r\n */\r\n getBins = async (): Promise<resGetBins | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}s`, {\r\n method: 'GET',\r\n headers: {\r\n 'Api-key': this.apiKey,\r\n Accept: 'application/json'\r\n }\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const counter = Number(response.headers.get('x-counter'));\r\n const data = await response.json();\r\n return {\r\n status: 'ok',\r\n callsDone: counter,\r\n callsRemaining: this.monthlyLimit - counter,\r\n bins: data\r\n };\r\n } catch (error) {\r\n console.error('getBins error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Syntactic sugar function for getBins to only return limit counter\r\n * @returns {object} <resGetStats | resError>\r\n */\r\n getStats = async (): Promise<resGetStats | resError> => {\r\n try {\r\n const res = await this.getBins();\r\n if (res.status === 'error') {\r\n throw new Error(`HTTP error! Status: ${(<resError>res).statusText}`);\r\n }\r\n\r\n delete (<resGetBins>res).bins;\r\n return <resGetStats>res;\r\n } catch (error) {\r\n console.error('getStats error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Reads the content of a certain bin ID\r\n * @param bin string Bin ID to read\r\n * @returns {object} <resGetBin | resError>\r\n */\r\n getBin = async (bin: string): Promise<resGetBin | resError> => {\r\n try {\r\n if (!bin) throw new Error('Bin ID missing!');\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'GET',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n Accept: 'application/json'\r\n }\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const data = await response.json();\r\n return {\r\n status: 'ok',\r\n data\r\n };\r\n } catch (error) {\r\n console.error('getBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Creates a new bin and saves the given JSON payload to it\r\n * @param payload {object} JSON object to create the bin with\r\n * @param keepPrivate {boolean} TRUE=bin shall be kept private\r\n * @returns {object} <resCreateBin | resError>\r\n */\r\n createBin = async (payload: object, keepPrivate: boolean): Promise<resCreateBin | resError> => {\r\n const headers = new Headers({\r\n 'Api-key': this.apiKey,\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json'\r\n });\r\n headers.append('Private', keepPrivate.toString());\r\n try {\r\n const response = await fetch(this.url, {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(payload)\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n let { uri, bin } = await response.json();\r\n if (!bin) bin = uri.split('/').pop();\r\n\r\n return {\r\n status: 'ok',\r\n uri,\r\n bin\r\n };\r\n } catch (error) {\r\n console.error('createBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Updates a certain bin with the given JSON object\r\n * @param payload {object} JSON object to update the bin with\r\n * @param bin string Bin ID to be updated\r\n * @returns {object} <resUpdateBin | resError>\r\n */\r\n updateBin = async (payload: object, bin: string): Promise<resUpdateBin | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'PUT',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json'\r\n },\r\n body: JSON.stringify(payload)\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const { data } = await response.json();\r\n return {\r\n status: 'ok',\r\n data\r\n };\r\n } catch (error) {\r\n console.error('updateBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Patches/inserts/changes a certain bin with the given JSON payload\r\n * ref: \"Partially update JSON\" at https://extendsclass.com/json-storage.html#apiDocumentation\r\n * @param payload {object} JSON object to patch the bin with\r\n * @param bin string Bin ID to be patched\r\n * @returns {object} <resPatchBin | resError>\r\n */\r\n patchBin = async (payload: object, bin: string): Promise<resPatchBin | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'PATCH',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/merge-patch+json',\r\n Accept: 'application/json'\r\n },\r\n body: JSON.stringify(payload)\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const { data } = await response.json();\r\n return {\r\n status: 'ok',\r\n data\r\n };\r\n } catch (error) {\r\n console.error('patchBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n\r\n /**\r\n * Deletes a certain bin\r\n * @param bin string Bin ID to be deleted\r\n * @returns {object} <resDeleteBin | resError>\r\n */\r\n deleteBin = async (bin: string): Promise<resDeleteBin | resError> => {\r\n try {\r\n const response = await fetch(`${this.url}/${bin}`, {\r\n method: 'DELETE',\r\n headers: {\r\n 'Security-key': this.securityKey,\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json'\r\n }\r\n });\r\n\r\n await this.validateResponse(response);\r\n\r\n const { status } = await response.json();\r\n if (status !== 0) throw new Error(`Bin delete failed with error: ${status.message}`);\r\n\r\n return {\r\n status: 'ok'\r\n };\r\n } catch (error) {\r\n console.error('deleteBin error:', error);\r\n return {\r\n status: 'error',\r\n statusText: <string>error\r\n };\r\n }\r\n };\r\n}\r\n"],"mappings":"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","names":["ECJsonStorage","apiKey","securityKey","url","response","counter","data","error","res","bin","payload","keepPrivate","headers","uri","status"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@neonwilderness/ecjsonstorage",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"exports": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.cts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"update": "ncu -u -x typescript && npm i",
|
|
10
|
+
"build": "npm run build:js && npm run build:dts",
|
|
11
|
+
"build:js": "tsup src/*.ts --format esm,cjs --minify",
|
|
12
|
+
"build:dts": "tsup src/index.ts --dts-only",
|
|
13
|
+
"deleteAllBins": "node dist/deleteAllBins.cjs",
|
|
14
|
+
"test": "vitest"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ExtendsClass JSON-Storage API wrapper"
|
|
18
|
+
],
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/index.*",
|
|
21
|
+
"src/index.ts",
|
|
22
|
+
"test/*",
|
|
23
|
+
".env.example",
|
|
24
|
+
"CHANGELOG.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"pack*.json",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/dotenv-safe": "^9.1.0",
|
|
32
|
+
"@types/node": "^26.6.3",
|
|
33
|
+
"dotenv": "^18.0.4",
|
|
34
|
+
"dotenv-safe": "^9.1.0",
|
|
35
|
+
"tsup": "^8.5.1",
|
|
36
|
+
"typescript": "^6.0.3",
|
|
37
|
+
"vitest": "^5.0.3"
|
|
38
|
+
},
|
|
39
|
+
"allowScripts": {
|
|
40
|
+
"esbuild@0.28.2": true
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESJsonStorage class is a wrapper to the ExtendsClass JSON-Storage API
|
|
3
|
+
*/
|
|
4
|
+
export type resGetBins = {
|
|
5
|
+
status: 'ok';
|
|
6
|
+
callsDone: number;
|
|
7
|
+
callsRemaining: number;
|
|
8
|
+
bins?: string[];
|
|
9
|
+
};
|
|
10
|
+
export type resGetStats = Omit<resGetBins, 'bins'>;
|
|
11
|
+
|
|
12
|
+
export type resGetBin = {
|
|
13
|
+
status: 'ok';
|
|
14
|
+
data: object;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type resUpdateBin = {
|
|
18
|
+
status: 'ok';
|
|
19
|
+
data: string;
|
|
20
|
+
};
|
|
21
|
+
export type resPatchBin = resUpdateBin;
|
|
22
|
+
|
|
23
|
+
export type resCreateBin = {
|
|
24
|
+
status: 'ok';
|
|
25
|
+
uri: string;
|
|
26
|
+
bin: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type resDeleteBin = Omit<resGetBin, 'data'>;
|
|
30
|
+
|
|
31
|
+
export type resError = {
|
|
32
|
+
status: 'error';
|
|
33
|
+
statusText: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export class ECJsonStorage {
|
|
37
|
+
url: string;
|
|
38
|
+
apiKey: string;
|
|
39
|
+
securityKey: string;
|
|
40
|
+
monthlyLimit: number = 10000;
|
|
41
|
+
|
|
42
|
+
constructor(apiKey: string, securityKey?: string, url?: string) {
|
|
43
|
+
this.url = url || 'https://json.extendsclass.com/bin';
|
|
44
|
+
this.apiKey = apiKey || '';
|
|
45
|
+
this.securityKey = securityKey || '';
|
|
46
|
+
|
|
47
|
+
if (!this.url || !this.apiKey) throw new Error('Invalid constructor params!');
|
|
48
|
+
while (this.url.endsWith('/')) {
|
|
49
|
+
this.url = this.url.slice(0, -1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Utility function to validate the response after the class function call
|
|
55
|
+
* @param response
|
|
56
|
+
*/
|
|
57
|
+
validateResponse = async (response: Response) => {
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const contentType = response.headers.get('content-type') || '';
|
|
63
|
+
if (contentType.startsWith('text/html')) {
|
|
64
|
+
throw new Error(`Expected JSON but server returned HTML: "${(await response.text()).slice(0, 100)}..."`);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get all bin IDs for a certain Api-key
|
|
70
|
+
* @returns {object} <resGetBins | resError>
|
|
71
|
+
*/
|
|
72
|
+
getBins = async (): Promise<resGetBins | resError> => {
|
|
73
|
+
try {
|
|
74
|
+
const response = await fetch(`${this.url}s`, {
|
|
75
|
+
method: 'GET',
|
|
76
|
+
headers: {
|
|
77
|
+
'Api-key': this.apiKey,
|
|
78
|
+
Accept: 'application/json'
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await this.validateResponse(response);
|
|
83
|
+
|
|
84
|
+
const counter = Number(response.headers.get('x-counter'));
|
|
85
|
+
const data = await response.json();
|
|
86
|
+
return {
|
|
87
|
+
status: 'ok',
|
|
88
|
+
callsDone: counter,
|
|
89
|
+
callsRemaining: this.monthlyLimit - counter,
|
|
90
|
+
bins: data
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('getBins error:', error);
|
|
94
|
+
return {
|
|
95
|
+
status: 'error',
|
|
96
|
+
statusText: <string>error
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Syntactic sugar function for getBins to only return limit counter
|
|
103
|
+
* @returns {object} <resGetStats | resError>
|
|
104
|
+
*/
|
|
105
|
+
getStats = async (): Promise<resGetStats | resError> => {
|
|
106
|
+
try {
|
|
107
|
+
const res = await this.getBins();
|
|
108
|
+
if (res.status === 'error') {
|
|
109
|
+
throw new Error(`HTTP error! Status: ${(<resError>res).statusText}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
delete (<resGetBins>res).bins;
|
|
113
|
+
return <resGetStats>res;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('getStats error:', error);
|
|
116
|
+
return {
|
|
117
|
+
status: 'error',
|
|
118
|
+
statusText: <string>error
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Reads the content of a certain bin ID
|
|
125
|
+
* @param bin string Bin ID to read
|
|
126
|
+
* @returns {object} <resGetBin | resError>
|
|
127
|
+
*/
|
|
128
|
+
getBin = async (bin: string): Promise<resGetBin | resError> => {
|
|
129
|
+
try {
|
|
130
|
+
if (!bin) throw new Error('Bin ID missing!');
|
|
131
|
+
const response = await fetch(`${this.url}/${bin}`, {
|
|
132
|
+
method: 'GET',
|
|
133
|
+
headers: {
|
|
134
|
+
'Security-key': this.securityKey,
|
|
135
|
+
Accept: 'application/json'
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
await this.validateResponse(response);
|
|
140
|
+
|
|
141
|
+
const data = await response.json();
|
|
142
|
+
return {
|
|
143
|
+
status: 'ok',
|
|
144
|
+
data
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('getBin error:', error);
|
|
148
|
+
return {
|
|
149
|
+
status: 'error',
|
|
150
|
+
statusText: <string>error
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Creates a new bin and saves the given JSON payload to it
|
|
157
|
+
* @param payload {object} JSON object to create the bin with
|
|
158
|
+
* @param keepPrivate {boolean} TRUE=bin shall be kept private
|
|
159
|
+
* @returns {object} <resCreateBin | resError>
|
|
160
|
+
*/
|
|
161
|
+
createBin = async (payload: object, keepPrivate: boolean): Promise<resCreateBin | resError> => {
|
|
162
|
+
const headers = new Headers({
|
|
163
|
+
'Api-key': this.apiKey,
|
|
164
|
+
'Security-key': this.securityKey,
|
|
165
|
+
'Content-Type': 'application/json',
|
|
166
|
+
Accept: 'application/json'
|
|
167
|
+
});
|
|
168
|
+
headers.append('Private', keepPrivate.toString());
|
|
169
|
+
try {
|
|
170
|
+
const response = await fetch(this.url, {
|
|
171
|
+
method: 'POST',
|
|
172
|
+
headers,
|
|
173
|
+
body: JSON.stringify(payload)
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await this.validateResponse(response);
|
|
177
|
+
|
|
178
|
+
let { uri, bin } = await response.json();
|
|
179
|
+
if (!bin) bin = uri.split('/').pop();
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
status: 'ok',
|
|
183
|
+
uri,
|
|
184
|
+
bin
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('createBin error:', error);
|
|
188
|
+
return {
|
|
189
|
+
status: 'error',
|
|
190
|
+
statusText: <string>error
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Updates a certain bin with the given JSON object
|
|
197
|
+
* @param payload {object} JSON object to update the bin with
|
|
198
|
+
* @param bin string Bin ID to be updated
|
|
199
|
+
* @returns {object} <resUpdateBin | resError>
|
|
200
|
+
*/
|
|
201
|
+
updateBin = async (payload: object, bin: string): Promise<resUpdateBin | resError> => {
|
|
202
|
+
try {
|
|
203
|
+
const response = await fetch(`${this.url}/${bin}`, {
|
|
204
|
+
method: 'PUT',
|
|
205
|
+
headers: {
|
|
206
|
+
'Security-key': this.securityKey,
|
|
207
|
+
'Content-Type': 'application/json',
|
|
208
|
+
Accept: 'application/json'
|
|
209
|
+
},
|
|
210
|
+
body: JSON.stringify(payload)
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await this.validateResponse(response);
|
|
214
|
+
|
|
215
|
+
const { data } = await response.json();
|
|
216
|
+
return {
|
|
217
|
+
status: 'ok',
|
|
218
|
+
data
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error('updateBin error:', error);
|
|
222
|
+
return {
|
|
223
|
+
status: 'error',
|
|
224
|
+
statusText: <string>error
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Patches/inserts/changes a certain bin with the given JSON payload
|
|
231
|
+
* ref: "Partially update JSON" at https://extendsclass.com/json-storage.html#apiDocumentation
|
|
232
|
+
* @param payload {object} JSON object to patch the bin with
|
|
233
|
+
* @param bin string Bin ID to be patched
|
|
234
|
+
* @returns {object} <resPatchBin | resError>
|
|
235
|
+
*/
|
|
236
|
+
patchBin = async (payload: object, bin: string): Promise<resPatchBin | resError> => {
|
|
237
|
+
try {
|
|
238
|
+
const response = await fetch(`${this.url}/${bin}`, {
|
|
239
|
+
method: 'PATCH',
|
|
240
|
+
headers: {
|
|
241
|
+
'Security-key': this.securityKey,
|
|
242
|
+
'Content-Type': 'application/merge-patch+json',
|
|
243
|
+
Accept: 'application/json'
|
|
244
|
+
},
|
|
245
|
+
body: JSON.stringify(payload)
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await this.validateResponse(response);
|
|
249
|
+
|
|
250
|
+
const { data } = await response.json();
|
|
251
|
+
return {
|
|
252
|
+
status: 'ok',
|
|
253
|
+
data
|
|
254
|
+
};
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error('patchBin error:', error);
|
|
257
|
+
return {
|
|
258
|
+
status: 'error',
|
|
259
|
+
statusText: <string>error
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Deletes a certain bin
|
|
266
|
+
* @param bin string Bin ID to be deleted
|
|
267
|
+
* @returns {object} <resDeleteBin | resError>
|
|
268
|
+
*/
|
|
269
|
+
deleteBin = async (bin: string): Promise<resDeleteBin | resError> => {
|
|
270
|
+
try {
|
|
271
|
+
const response = await fetch(`${this.url}/${bin}`, {
|
|
272
|
+
method: 'DELETE',
|
|
273
|
+
headers: {
|
|
274
|
+
'Security-key': this.securityKey,
|
|
275
|
+
'Content-Type': 'application/json',
|
|
276
|
+
Accept: 'application/json'
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
await this.validateResponse(response);
|
|
281
|
+
|
|
282
|
+
const { status } = await response.json();
|
|
283
|
+
if (status !== 0) throw new Error(`Bin delete failed with error: ${status.message}`);
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
status: 'ok'
|
|
287
|
+
};
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error('deleteBin error:', error);
|
|
290
|
+
return {
|
|
291
|
+
status: 'error',
|
|
292
|
+
statusText: <string>error
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
ECJsonStorage,
|
|
4
|
+
type resGetBins,
|
|
5
|
+
type resGetBin,
|
|
6
|
+
type resCreateBin,
|
|
7
|
+
type resUpdateBin,
|
|
8
|
+
type resPatchBin
|
|
9
|
+
} from '../src/index';
|
|
10
|
+
import { config } from 'dotenv-safe';
|
|
11
|
+
config();
|
|
12
|
+
|
|
13
|
+
test('Rejects a constructor call with a missing Api-key', () => {
|
|
14
|
+
expect(() => {
|
|
15
|
+
const json = new ECJsonStorage('');
|
|
16
|
+
}).toThrow('Invalid constructor params!');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('Fixes an alternative Api URL if it has ending slash/es', () => {
|
|
20
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!, 'https://other.com/api////');
|
|
21
|
+
expect(json.url).toBe('https://other.com/api');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test.only('Can construct a valid class', () => {
|
|
25
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
26
|
+
expect(json.url).toBe('https://json.extendsclass.com/bin');
|
|
27
|
+
expect(json.apiKey).toBe(process.env.JSONAPI);
|
|
28
|
+
expect(json.securityKey).toBe(process.env.JSONSEC);
|
|
29
|
+
expect(json.monthlyLimit).toBe(10000);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Can overwrite the API url if needed', () => {
|
|
33
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!, 'https://someotherapiurl.de');
|
|
34
|
+
expect(json.url).toBe('https://someotherapiurl.de');
|
|
35
|
+
expect(json.apiKey).toBe(process.env.JSONAPI);
|
|
36
|
+
expect(json.securityKey).toBe(process.env.JSONSEC);
|
|
37
|
+
expect(json.monthlyLimit).toBe(10000);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('Can get all bins of an API Key', async () => {
|
|
41
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
42
|
+
const res = await json.getBins();
|
|
43
|
+
console.log(res);
|
|
44
|
+
expect(res.status).toBe('ok');
|
|
45
|
+
expect((res as resGetBins).callsDone).not.toBeNaN();
|
|
46
|
+
expect((res as resGetBins).callsDone).toBeGreaterThan(0);
|
|
47
|
+
expect((res as resGetBins).callsRemaining).not.toBeNaN();
|
|
48
|
+
expect((res as resGetBins).callsRemaining).toBeGreaterThan(0);
|
|
49
|
+
expect(Array.isArray((res as resGetBins).bins)).toBe(true);
|
|
50
|
+
expect((res as resGetBins).bins!.length).toBeGreaterThanOrEqual(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('Can get the current limit state', async () => {
|
|
54
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
55
|
+
const res = await json.getStats();
|
|
56
|
+
console.log(res);
|
|
57
|
+
expect(res.status).toBe('ok');
|
|
58
|
+
expect((res as resGetBins).callsDone).not.toBeNaN();
|
|
59
|
+
expect((res as resGetBins).callsDone).toBeGreaterThan(0);
|
|
60
|
+
expect((res as resGetBins).callsRemaining).not.toBeNaN();
|
|
61
|
+
expect((res as resGetBins).callsRemaining).toBeGreaterThan(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('Can get the content of a bin', async () => {
|
|
65
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
66
|
+
const res = await json.getBin(process.env.JSONBIN!);
|
|
67
|
+
console.log(res);
|
|
68
|
+
expect(res.status).toBe('ok');
|
|
69
|
+
expect(typeof (res as resGetBin).data).toBe('object');
|
|
70
|
+
expect(Object.keys((res as resGetBin).data).length).toBe(2);
|
|
71
|
+
expect(Object.keys((res as resGetBin).data)).toContain('name');
|
|
72
|
+
expect(Object.keys((res as resGetBin).data)).toContain('profession');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('Can create a new bin', async () => {
|
|
76
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
77
|
+
const payload = { name: 'Neon', profession: 'IT-Consultant' };
|
|
78
|
+
const res = await json.createBin(payload, true);
|
|
79
|
+
console.log(res);
|
|
80
|
+
expect(res.status).toBe('ok');
|
|
81
|
+
expect(typeof (res as resCreateBin).uri).toBeTruthy();
|
|
82
|
+
expect(typeof (res as resCreateBin).bin).toBeTruthy();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
interface payloadData {
|
|
86
|
+
name: string;
|
|
87
|
+
profession: string;
|
|
88
|
+
}
|
|
89
|
+
test('Can update/change the content of a bin', async () => {
|
|
90
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
91
|
+
const payload: payloadData = { name: 'NeonWilderness', profession: 'Web Ninja' };
|
|
92
|
+
const res = await json.updateBin(payload, process.env.JSONBIN!);
|
|
93
|
+
console.log(res);
|
|
94
|
+
expect(res.status).toBe('ok');
|
|
95
|
+
const dataParsed = JSON.parse((res as resUpdateBin).data);
|
|
96
|
+
expect(Object.keys(dataParsed).length).toBe(2);
|
|
97
|
+
expect(Object.keys(dataParsed)).toContain('name');
|
|
98
|
+
expect(Object.keys(dataParsed)).toContain('profession');
|
|
99
|
+
const { name, profession } = dataParsed as payloadData;
|
|
100
|
+
expect(name).toBe(payload.name);
|
|
101
|
+
expect(profession).toBe(payload.profession);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
interface payloadPatched extends payloadData {
|
|
105
|
+
added: string;
|
|
106
|
+
times: number;
|
|
107
|
+
}
|
|
108
|
+
test('Can patch the content of a bin', async () => {
|
|
109
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
110
|
+
const payloadAdd = { added: 'IT-Consulting', times: 999 };
|
|
111
|
+
const res = await json.patchBin(payloadAdd, process.env.JSONBIN!);
|
|
112
|
+
console.log(res);
|
|
113
|
+
expect(res.status).toBe('ok');
|
|
114
|
+
const data = JSON.parse((res as resPatchBin).data);
|
|
115
|
+
expect(Object.keys(data).length).toBe(4);
|
|
116
|
+
expect(Object.keys(data)).toContain('name');
|
|
117
|
+
expect(Object.keys(data)).toContain('profession');
|
|
118
|
+
expect(Object.keys(data)).toContain('added');
|
|
119
|
+
expect(Object.keys(data)).toContain('times');
|
|
120
|
+
const { name, profession, added, times } = data as payloadPatched;
|
|
121
|
+
expect(name).toBe('NeonWilderness');
|
|
122
|
+
expect(profession).toBe('Web Ninja');
|
|
123
|
+
expect(added).toBe('IT-Consulting');
|
|
124
|
+
expect(times).toBe(999);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('Can delete a bin', async () => {
|
|
128
|
+
const json = new ECJsonStorage(process.env.JSONAPI!, process.env.JSONSEC!);
|
|
129
|
+
const res1 = await json.createBin({ type: 'dummy' }, true);
|
|
130
|
+
expect(res1.status).toBe('ok');
|
|
131
|
+
const bin = (res1 as resCreateBin).bin;
|
|
132
|
+
expect(bin).toBeTruthy();
|
|
133
|
+
console.log(bin);
|
|
134
|
+
const res2 = await json.deleteBin(bin);
|
|
135
|
+
expect(res2.status).toBe('ok');
|
|
136
|
+
});
|