@plattar/sdk-core
Advanced tools
Comparing version
@@ -40,3 +40,3 @@ import { CoreObject, CoreObjectAttributes } from '../core-object'; | ||
*/ | ||
protected _Fetch(url: string, type: QueryFetchType): Promise<Array<T>>; | ||
protected _Fetch<Output extends CoreObject<U>, U extends CoreObjectAttributes>(output: Output, url: string, type: QueryFetchType): Promise<Array<Output>>; | ||
/** | ||
@@ -46,3 +46,3 @@ * Generates the url sequence of all queries and returns | ||
toString(): string; | ||
static fetch<T extends CoreObject<CoreObjectAttributes>>(service: Service, instance: T, encodedURL: string, type: QueryFetchType, abort?: AbortSignal): Promise<Array<T>>; | ||
static fetch<Input extends CoreObject<CoreObjectAttributes>, Output extends CoreObject<CoreObjectAttributes>>(service: Service, input: Input, output: Output, encodedURL: string, type: QueryFetchType, abort?: AbortSignal): Promise<Array<Output>>; | ||
} |
@@ -114,4 +114,4 @@ "use strict"; | ||
*/ | ||
async _Fetch(url, type) { | ||
return CoreQuery.fetch(this.service, this.instance, encodeURI(`${url}${this._queries.length > 0 ? `?${this.toString()}` : ''}`), type, this._abort.signal); | ||
async _Fetch(output, url, type) { | ||
return CoreQuery.fetch(this.service, this.instance, output, encodeURI(`${url}${this._queries.length > 0 ? `?${this.toString()}` : ''}`), type, this._abort.signal); | ||
} | ||
@@ -136,3 +136,3 @@ /** | ||
} | ||
static async fetch(service, instance, encodedURL, type, abort) { | ||
static async fetch(service, input, output, encodedURL, type, abort) { | ||
const results = new Array(); | ||
@@ -196,3 +196,3 @@ if (!fetch) { | ||
case 'PATCH': | ||
request.body = JSON.stringify(instance.payload); | ||
request.body = JSON.stringify(input.payload); | ||
break; | ||
@@ -290,5 +290,5 @@ } | ||
// add the first object to the instance | ||
cache.set(object.id, instance); | ||
cache.set(object.id, output); | ||
// construct the first object | ||
instance.setFromAPI({ | ||
output.setFromAPI({ | ||
object: object, | ||
@@ -298,3 +298,3 @@ includes: includesMap, | ||
}); | ||
results.push(instance); | ||
results.push(output); | ||
// begin construction of every other instance | ||
@@ -335,5 +335,5 @@ for (let i = 1; i < listRecords.length; i++) { | ||
// add the first object to the instance | ||
cache.set(record.id, instance); | ||
cache.set(record.id, output); | ||
// construct the first object | ||
instance.setFromAPI({ | ||
output.setFromAPI({ | ||
object: record, | ||
@@ -343,3 +343,3 @@ includes: includesMap, | ||
}); | ||
results.push(instance); | ||
results.push(output); | ||
} | ||
@@ -346,0 +346,0 @@ } |
@@ -102,4 +102,5 @@ "use strict"; | ||
const connection = service || service_1.Service.default; | ||
const newObjectInstance = objectType.newInstance(); | ||
// otherwise we need to fetch and cache the relations directly | ||
const results = await core_query_1.CoreQuery.fetch(connection, objectType.newInstance(), `${connection.url}/${this._instance.type}/${this._instance.id}/${objectType.type}`, 'GET'); | ||
const results = await core_query_1.CoreQuery.fetch(connection, newObjectInstance, newObjectInstance, `${connection.url}/${this._instance.type}/${this._instance.id}/${objectType.type}`, 'GET'); | ||
// add the results into the cache | ||
@@ -106,0 +107,0 @@ this.cache.put(objectType.type, results); |
@@ -10,2 +10,4 @@ "use strict"; | ||
const fs_1 = __importDefault(require("fs")); | ||
const schema_list_1 = require("./generators/schema-list"); | ||
const schema_collection_1 = require("./generators/schema-collection"); | ||
class Generator { | ||
@@ -15,7 +17,13 @@ static async generate(data) { | ||
const project = project_1.Project.generate(data.package); | ||
const schemas = new Array(); | ||
// generate the schema source files | ||
const schemas = new schema_list_1.SchemaList(); | ||
const collections = new schema_collection_1.SchemaCollection(); | ||
// add all schemas into the collections pool | ||
data.controllers.forEach((controller) => { | ||
schemas.push(schema_1.Schema.generate((new controller()))); | ||
collections.push((new controller())); | ||
}); | ||
// generate the schema source files | ||
// each schema can generate multiple schema files based on endpoints | ||
collections.forEach((schema, endpoints) => { | ||
schemas.push(new schema_1.Schema().generate(schema, endpoints)); | ||
}); | ||
const outputDir = `./${data.output}/${data.package.name}-sdk`; | ||
@@ -22,0 +30,0 @@ // ensure project folder exists |
@@ -1,7 +0,3 @@ | ||
import { CoreController } from '@plattar/api-core'; | ||
export interface GeneratedSchema { | ||
readonly name: string; | ||
readonly fname: string; | ||
readonly data: string; | ||
} | ||
import { ObjectSchema } from '@plattar/api-core'; | ||
import { EndpointMapping } from './schema-collection'; | ||
/** | ||
@@ -11,8 +7,15 @@ * Contains helpful functions to generate Classes from a CoreController schema definition from the core-api | ||
export declare class Schema { | ||
private _name; | ||
private _fname; | ||
private _data; | ||
private _key; | ||
constructor(); | ||
get key(): string; | ||
get name(): string; | ||
get fname(): string; | ||
get data(): string; | ||
/** | ||
* Generates a class using the provided controller | ||
*/ | ||
static generate(controller: CoreController): GeneratedSchema; | ||
private static generateDynamicQueryFunctions; | ||
private static generateStaticQueryFunctions; | ||
generate(schema: typeof ObjectSchema, endpoints: Array<EndpointMapping>): Schema; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Schema = void 0; | ||
const api_core_1 = require("@plattar/api-core"); | ||
const util_1 = require("./util"); | ||
const attribute_generator_1 = require("./schemas/attribute-generator"); | ||
const static_query_generator_1 = require("./schemas/static-query-generator"); | ||
const dynamic_query_generator_1 = require("./schemas/dynamic-query-generator"); | ||
/** | ||
@@ -10,40 +12,64 @@ * Contains helpful functions to generate Classes from a CoreController schema definition from the core-api | ||
class Schema { | ||
_name; | ||
_fname; | ||
_data; | ||
_key; | ||
constructor() { | ||
this._name = null; | ||
this._fname = null; | ||
this._data = null; | ||
this._key = null; | ||
} | ||
get key() { | ||
if (!this._key) { | ||
throw new Error("Schema.key is not defined - use .generate() function"); | ||
} | ||
return this._key; | ||
} | ||
get name() { | ||
if (!this._name) { | ||
throw new Error("Schema.name is not defined - use .generate() function"); | ||
} | ||
return this._name; | ||
} | ||
get fname() { | ||
if (!this._fname) { | ||
throw new Error("Schema.fname is not defined - use .generate() function"); | ||
} | ||
return this._fname; | ||
} | ||
get data() { | ||
if (!this._data) { | ||
throw new Error("Schema.data is not defined - use .generate() function"); | ||
} | ||
return this._data; | ||
} | ||
/** | ||
* Generates a class using the provided controller | ||
*/ | ||
static generate(controller) { | ||
const schemaInstance = new (controller.getSchema()); | ||
generate(schema, endpoints) { | ||
// create a list of all imports | ||
const imports = new Set(); | ||
endpoints.forEach((value) => { | ||
if (value.mount.meta.input && value.mount.meta.input !== schema) { | ||
imports.add(`import {${util_1.Util.capitaliseClassName(value.mount.meta.input.type)},${util_1.Util.getClassAttributesName(value.mount.meta.input.type)}} from './${util_1.Util.getNameFromApiType(value.mount.meta.input.type)}';`); | ||
} | ||
if (value.mount.meta.output && value.mount.meta.output !== schema) { | ||
imports.add(`import {${util_1.Util.capitaliseClassName(value.mount.meta.output.type)},${util_1.Util.getClassAttributesName(value.mount.meta.output.type)}} from './${util_1.Util.getNameFromApiType(value.mount.meta.output.type)}';`); | ||
} | ||
}); | ||
let output = ''; | ||
imports.forEach((value) => { | ||
output += `${value}\n`; | ||
}); | ||
const schemaInstance = new schema; | ||
const className = util_1.Util.capitaliseClassName(schemaInstance.type); | ||
const interfaceName = className + 'Attributes'; | ||
const interfaceName = util_1.Util.getClassAttributesName(schemaInstance.type); | ||
const queryName = className + 'Query'; | ||
const fileInterfaceName = className + 'FileAttributes'; | ||
const isFile = (schemaInstance instanceof api_core_1.FileSchema) ? true : false; | ||
// GENERATE: import statement | ||
let output = `import { CoreObject, CoreObjectAttributes, GlobalObjectPool, Service, ${(isFile ? 'CoreFileQuery, CoreFileAttributes' : 'CoreQuery')} } from '@plattar/sdk-core';\n`; | ||
// GENERATE: file-attributes (if any) | ||
if (isFile) { | ||
const fileSchema = schemaInstance; | ||
output += `export interface ${fileInterfaceName} extends CoreFileAttributes {\n`; | ||
fileSchema.getFileUploads().forEach((value) => { | ||
output += `\treadonly ${value.key}:any\n`; | ||
}); | ||
output += '}\n'; | ||
} | ||
// GENERATE: attributes interface, public is mutable and protected is immutable | ||
output += `export interface ${interfaceName} extends CoreObjectAttributes {\n`; | ||
schemaInstance.attributes.publicListTypes.forEach((attribute) => { | ||
output += `\t${attribute.key}${attribute.type !== 'any' ? '?' : ''}:${attribute.type};\n`; | ||
}); | ||
schemaInstance.attributes.protectedListTypes.forEach((attribute) => { | ||
output += `\treadonly ${attribute.key}${attribute.type !== 'any' ? '?' : ''}:${attribute.type};\n`; | ||
}); | ||
output += '}\n'; | ||
// GENERATE: the main Static Query object + all functions (functions to-do) | ||
output += `export class ${queryName}Static extends ${(isFile ? 'CoreFileQuery' : 'CoreQuery')}<${className},${interfaceName}${(isFile ? `,${fileInterfaceName}` : '')}> {\n`; | ||
output += this.generateStaticQueryFunctions(controller, className); | ||
output += '}\n'; | ||
// GENERATE: the main Dynamic Query object + all functions (functions to-do) | ||
output += `export class ${queryName}Dynamic extends ${(isFile ? 'CoreFileQuery' : 'CoreQuery')}<${className},${interfaceName}${(isFile ? `,${fileInterfaceName}` : '')}> {\n`; | ||
output += this.generateDynamicQueryFunctions(controller, className); | ||
output += '}\n'; | ||
// generate attributes | ||
output += attribute_generator_1.AttributeGenerator.generate(schema); | ||
// generate static query interfaces | ||
output += static_query_generator_1.StaticQueryGenerator.generate(schema, endpoints); | ||
// generate dynamic query interfaces | ||
output += dynamic_query_generator_1.DynamicQueryGenerator.generate(schema, endpoints); | ||
// GENERATE: the main class | ||
@@ -61,86 +87,9 @@ output += `export class ${className} extends CoreObject<${interfaceName}> {\n`; | ||
output += `}\nGlobalObjectPool.register(${className});\n`; | ||
return { | ||
name: schemaInstance.type.replaceAll('_', '-'), | ||
fname: schemaInstance.type.replaceAll('_', '-') + '.ts', | ||
data: output | ||
}; | ||
this._key = schemaInstance.type; | ||
this._name = schemaInstance.type.replaceAll('_', '-'); | ||
this._fname = schemaInstance.type.replaceAll('_', '-') + '.ts'; | ||
this._data = output; | ||
return this; | ||
} | ||
static generateDynamicQueryFunctions(controller, className) { | ||
const mounts = controller.mount(); | ||
const apiType = controller.getSchema().apiType; | ||
const suffix = controller.suffix; | ||
let endpoint = ""; | ||
if (suffix !== "") { | ||
endpoint = endpoint + "/" + suffix; | ||
} | ||
if (apiType !== api_core_1.Util.DEFAULT_OBJECT_TYPE) { | ||
endpoint = endpoint + "/" + apiType; | ||
} | ||
let output = ''; | ||
mounts.forEach((mount) => { | ||
const meta = mount.meta; | ||
if (meta && meta.returnType === "single") { | ||
// generate the function | ||
const data = util_1.Util.getParams(mount.endpoint); | ||
let dataType = null; | ||
let isSet = false; | ||
let additionalQuery = ''; | ||
if (data.length > 0) { | ||
dataType = '{'; | ||
data.forEach((attr) => { | ||
if (attr !== 'id') { | ||
dataType += `${attr}:string,`; | ||
isSet = true; | ||
additionalQuery += `.replace(':${attr}', params.${attr})`; | ||
} | ||
}); | ||
dataType = (dataType.slice(0, -1) + '}'); | ||
} | ||
const mountEndpoint = mount.endpoint.replace(":id", "${this.instance.id}"); | ||
output += `\tpublic async ${meta.name}(${(isSet ? `params:${dataType}` : '')}): Promise<${className} | null> {\n`; | ||
output += `\t\tconst url:string = \`${endpoint}${mountEndpoint}\`${isSet ? additionalQuery : ''};\n`; | ||
output += `\t\tconst result:Array<${className}> = await this._Fetch(url, '${mount.type}');\n`; | ||
output += `\t\treturn result.length > 0 ? result[0] : null;\n`; | ||
output += '\t}\n'; | ||
} | ||
}); | ||
return output; | ||
} | ||
static generateStaticQueryFunctions(controller, className) { | ||
const mounts = controller.mount(); | ||
const apiType = controller.getSchema().apiType; | ||
const suffix = controller.suffix; | ||
let endpoint = ""; | ||
if (suffix !== "") { | ||
endpoint = endpoint + "/" + suffix; | ||
} | ||
if (apiType !== api_core_1.Util.DEFAULT_OBJECT_TYPE) { | ||
endpoint = endpoint + "/" + apiType; | ||
} | ||
let output = ''; | ||
mounts.forEach((mount) => { | ||
const meta = mount.meta; | ||
if (meta && meta.returnType !== "custom") { | ||
// generate the function | ||
const data = util_1.Util.getParams(mount.endpoint); | ||
let dataType = null; | ||
let additionalQuery = ''; | ||
if (data.length > 0) { | ||
dataType = '{'; | ||
data.forEach((attr) => { | ||
dataType += `${attr}:string,`; | ||
additionalQuery += `.replace(':${attr}', params.${attr})`; | ||
}); | ||
dataType = (dataType.slice(0, -1) + '}'); | ||
} | ||
output += `\tpublic async ${meta.name}(${(dataType ? `params:${dataType}` : '')}): Promise<${meta.returnType === "array" ? `Array<${className}>` : `${className} | null`}> {\n`; | ||
output += `\t\tconst url:string = \`${endpoint}${mount.endpoint}\`${dataType ? additionalQuery : ''};\n`; | ||
output += `\t\tconst result:Array<${className}> = await this._Fetch(url, '${mount.type}');\n`; | ||
output += `\t\t${meta.returnType === "array" ? 'return result' : 'return result.length > 0 ? result[0] : null'};\n`; | ||
output += '\t}\n'; | ||
} | ||
}); | ||
return output; | ||
} | ||
} | ||
exports.Schema = Schema; |
@@ -7,2 +7,5 @@ export declare class Util { | ||
static capitaliseClassName(name: string): string; | ||
static getClassAttributesName(name: string): string; | ||
static getNameFromApiType(type: string): string; | ||
static getFileNameFromApiType(type: string): string; | ||
/** | ||
@@ -9,0 +12,0 @@ * Returns the extracted ID's from a URL schema, for example |
@@ -17,2 +17,11 @@ "use strict"; | ||
} | ||
static getClassAttributesName(name) { | ||
return `${Util.capitaliseClassName(name)}Attributes`; | ||
} | ||
static getNameFromApiType(type) { | ||
return type.replaceAll('_', '-'); | ||
} | ||
static getFileNameFromApiType(type) { | ||
return `${Util.getNameFromApiType(type)}.ts`; | ||
} | ||
/** | ||
@@ -19,0 +28,0 @@ * Returns the extracted ID's from a URL schema, for example |
@@ -1,2 +0,2 @@ | ||
declare const _default: "1.165.1"; | ||
declare const _default: "1.166.1"; | ||
export default _default; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.default = "1.165.1"; | ||
exports.default = "1.166.1"; |
{ | ||
"name": "@plattar/sdk-core", | ||
"version": "1.165.1", | ||
"version": "1.166.1", | ||
"description": "Core SDK Module for Generative SDK using API Core", | ||
@@ -48,5 +48,5 @@ "main": "dist/index.js", | ||
"devDependencies": { | ||
"typescript": "^5.2.2", | ||
"@plattar/api-core": "^1.163.5", | ||
"@types/node": "^18.16.0", | ||
"typescript": "^5.3.3", | ||
"@plattar/api-core": "^1.166.2", | ||
"@types/node": "^20.11.13", | ||
"madge": "^6.1.0" | ||
@@ -53,0 +53,0 @@ }, |
125
README.md
@@ -7,6 +7,8 @@ <h3 align="center"> | ||
_sdk-core_ is a generative & runtime support module for automatically generating a TypeScript SDK to interface with Plattar backend services | ||
## About | ||
### Installation | ||
Facilitating Seamless Integration with Plattar Backend Services through Automated TypeScript SDK Generation and Runtime Support | ||
## Installation | ||
- Install using [npm](https://www.npmjs.com/package/@plattar/sdk-core) | ||
@@ -16,2 +18,119 @@ | ||
npm install @plattar/sdk-core | ||
``` | ||
``` | ||
## Examples | ||
Utilize the `@plattar/sample-sdk` example featuring `Scene`, `Application`, `Page` and `File` objects as references. Subsequently, substitute these samples with the corresponding objects from the SDK you are currently working with. | ||
> [!IMPORTANT] | ||
> Kindly be aware that the objects employed in these illustrations may vary based on the generated SDK produced by this module. | ||
### Service Configuration | ||
Use `Service.config()` to set up a default global configuration that will be applied to all objects. Initialization options include unauthenticated, cookie authenticated, or token-based authentication. | ||
#### Configuring Default Service without Authentication | ||
```typescript | ||
import { Service } from "@plattar/sample-sdk"; | ||
Service.config({ | ||
url: 'https://api.plattar.com' | ||
}); | ||
``` | ||
#### Configuring Default Service with Cookie Authentication | ||
```typescript | ||
import { Service } from "@plattar/sample-sdk"; | ||
Service.config({ | ||
url: 'https://api.plattar.com', | ||
auth: { | ||
type: 'cookie' | ||
} | ||
}); | ||
``` | ||
#### Configuring Default Service with Token Authentication | ||
```typescript | ||
import { Service } from "@plattar/sample-sdk"; | ||
Service.config({ | ||
url: 'https://api.plattar.com', | ||
auth: { | ||
type: 'token', | ||
token: 'your-plattar-auth-token' | ||
} | ||
}); | ||
``` | ||
### Handling Service Errors | ||
The Service offers multiple error-handling configuration options. By default, errors are logged using `console.error()`. Your available options include: | ||
- `silent`: Does not log or throw any errors and silently returns. | ||
- `console.error`: Logs the error using `console.error()` and returns. | ||
- `console.warn`: Logs the error using `console.warn()` and returns. | ||
- `throw`: Throws the error, requiring you to catch it using a `try/catch` clause. | ||
```typescript | ||
import { Service } from "@plattar/sample-sdk"; | ||
Service.config({ | ||
url: 'https://api.plattar.com', | ||
options: { | ||
errorHandler: 'silent' | ||
} | ||
}); | ||
``` | ||
You have the option to supply your own error listener, which receives all errors irrespective of the errorHandler setting. This feature is beneficial for analytics or serving as a global catch-all. It is set to null by default. | ||
```typescript | ||
import { Service, CoreError } from "@plattar/sample-sdk"; | ||
Service.config({ | ||
url: 'https://api.plattar.com', | ||
options: { | ||
errorHandler: 'silent', | ||
errorListener: (error:CoreError) => { | ||
console.error(error); | ||
} | ||
} | ||
}); | ||
``` | ||
### Basic Object Queries | ||
Employ the predefined objects to make API queries. Each SDK comes with its unique set of objects and query functions. Consult the documentation of the SDK you are using for detailed information. | ||
#### Individual Object Query | ||
Some queries exclusively yield a single object instance. In such cases, the result will either be the `object` or `null`. | ||
```typescript | ||
import { Scene } from "@plattar/sample-sdk"; | ||
const myScene:Scene | null = await new Scene("your-scene-id").query().get(); | ||
``` | ||
Alternatively, you have the option to execute the same query using the following approach | ||
```typescript | ||
import { Scene } from "@plattar/sample-sdk"; | ||
const myScene:Scene | null = await Scene.query().get({id: "your-scene-id" }); | ||
``` | ||
#### Multiple Object Query | ||
Some queries result in multiple objects due to the query type. In these instances, the outcome will be an `array`. | ||
```typescript | ||
import { Scene } from "@plattar/sample-sdk"; | ||
const myScenes:Array<Scene> = await Scene("your-scene-id").query().list(); | ||
``` | ||
94584
19.4%61
19.61%2264
13.77%135
800%