Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version/0.14.0 #33

Merged
merged 3 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@comake/skl-js-engine",
"version": "0.13.4",
"version": "0.14.0",
"description": "Standard Knowledge Language Javascript Engine",
"keywords": [
"skl",
Expand Down Expand Up @@ -43,7 +43,7 @@
},
"dependencies": {
"@comake/openapi-operation-executor": "^0.11.1",
"@comake/rmlmapper-js": "^0.4.0",
"@comake/rmlmapper-js": "^0.5.1",
"@rdfjs/data-model": "^1.3.0",
"jsonld": "^8.1.0",
"jsonpath-plus": "^7.2.0",
Expand Down
106 changes: 82 additions & 24 deletions src/SklEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export type VerbInterface = Record<string, VerbHandler>;
export type MappingResponseOption<T extends boolean> = T extends true ? JSONObject : NodeObject;

export class SKLEngine {
private readonly mapper: Mapper;
private readonly adapter: QueryAdapter;
private readonly functions?: Record<string, (args: any | any[]) => any>;
private readonly inputFiles?: Record<string, string>;
private readonly globalCallbacks?: Callbacks;
private readonly disableValidation?: boolean;
Expand All @@ -77,7 +77,7 @@ export class SKLEngine {
this.disableValidation = options.disableValidation;
this.globalCallbacks = options.callbacks;
this.inputFiles = options.inputFiles;
this.mapper = new Mapper({ functions: options.functions });
this.functions = options.functions;

// eslint-disable-next-line func-style
const getVerbHandler = (getTarget: VerbInterface, property: string): VerbHandler =>
Expand Down Expand Up @@ -164,8 +164,14 @@ export class SKLEngine {
args: JSONObject,
mapping: OrArray<NodeObject>,
frame?: Record<string, any>,
verbConfig?: VerbConfig,
): Promise<NodeObject> {
return await this.mapper.apply(args, mapping, frame ?? {});
const functions = {
...this.functions,
...verbConfig?.functions,
};
const mapper = new Mapper({ functions });
return await mapper.apply(args, mapping, frame ?? {});
}

public async executeTrigger(
Expand All @@ -176,6 +182,7 @@ export class SKLEngine {
const verbArgs = await this.performParameterMappingOnArgsIfDefined(
payload,
triggerToVerbMapping,
undefined,
false,
);
const verbId = await this.performVerbMappingWithArgs(payload, triggerToVerbMapping);
Expand Down Expand Up @@ -223,6 +230,7 @@ export class SKLEngine {
verbReturnValue = await this.performReturnValueMappingWithFrame(
verbArgs,
verb as MappingWithReturnValueMapping,
verbConfig,
);
} else if (SKL.series in verb) {
verbReturnValue = await this.executeSeriesVerb(verb, verbArgs, verbConfig);
Expand Down Expand Up @@ -296,10 +304,15 @@ export class SKLEngine {
args: JSONObject,
verbConfig?: VerbConfig,
): Promise<OrArray<NodeObject>> {
args = await this.addPreProcessingMappingToArgs(verbMapping, args);
const verbId = await this.performVerbMappingWithArgs(args, verbMapping);
args = await this.addPreProcessingMappingToArgs(verbMapping, args, verbConfig);
const verbId = await this.performVerbMappingWithArgs(args, verbMapping, verbConfig);
if (verbId) {
const verbArgs = await this.performParameterMappingOnArgsIfDefined({ ...args, verbId }, verbMapping, false);
const verbArgs = await this.performParameterMappingOnArgsIfDefined(
{ ...args, verbId },
verbMapping,
verbConfig,
false,
);
if (verbId === SKL_ENGINE.update) {
await this.updateEntityFromVerbArgs(verbArgs);
return {};
Expand Down Expand Up @@ -327,19 +340,25 @@ export class SKLEngine {
return await this.performReturnValueMappingWithFrame(
returnValue as JSONObject,
verbMapping as MappingWithReturnValueMapping,
verbConfig,
);
}
return returnValue;
}
return {};
}

private async addPreProcessingMappingToArgs(verbMapping: VerbMapping, args: JSONObject): Promise<JSONObject> {
private async addPreProcessingMappingToArgs(
verbMapping: VerbMapping,
args: JSONObject,
verbConfig?: VerbConfig,
): Promise<JSONObject> {
if (SKL.preProcessingMapping in verbMapping) {
const preMappingArgs = await this.performMapping(
args,
verbMapping[SKL.preProcessingMapping] as NodeObject,
getValueIfDefined(verbMapping[SKL.preProcessingMappingFrame]),
verbConfig,
);
return { ...args, preProcessedParameters: preMappingArgs as JSONObject };
}
Expand Down Expand Up @@ -422,9 +441,10 @@ export class SKLEngine {
const operationArgs = await this.performParameterMappingOnArgsIfDefined(
args,
mapping as MappingWithParameterMapping,
verbConfig,
);
const operationInfo = await this.performOperationMappingWithArgs(args, mapping);
const rawReturnValue = await this.performOperation(operationInfo, operationArgs, account);
const operationInfo = await this.performOperationMappingWithArgs(args, mapping, verbConfig);
const rawReturnValue = await this.performOperation(operationInfo, operationArgs, account, undefined, verbConfig);
if (operationInfo[SKL.schemeName] && rawReturnValue.data.authorizationUrl) {
return {
'@type': '@json',
Expand All @@ -436,6 +456,7 @@ export class SKLEngine {
const mappedReturnValue = await this.performReturnValueMappingWithFrame(
rawReturnValue,
mapping as MappingWithReturnValueMapping,
verbConfig,
verb,
);
if (shouldValidate) {
Expand All @@ -457,18 +478,28 @@ export class SKLEngine {
private async performOperationMappingWithArgs(
args: JSONObject,
mapping: MappingWithOperationMapping,
verbConfig?: VerbConfig,
): Promise<NodeObject> {
if (mapping[SKL.operationId]) {
return { [SKL.operationId]: mapping[SKL.operationId] };
}
return await this.performMapping(args, mapping[SKL.operationMapping] as OrArray<NodeObject>);
if (mapping[SKL.dataSource]) {
return { [SKL.dataSource]: mapping[SKL.dataSource] };
}
return await this.performMapping(
args,
mapping[SKL.operationMapping] as OrArray<NodeObject>,
undefined,
verbConfig,
);
}

private async performOperation(
operationInfo: NodeObject,
operationArgs: JSONObject,
account: Entity,
securityCredentials?: Entity,
verbConfig?: VerbConfig,
): Promise<OperationResponse> {
if (operationInfo[SKL.schemeName]) {
return await this.performOauthSecuritySchemeStageWithCredentials(
Expand All @@ -480,7 +511,8 @@ export class SKLEngine {
}
if (operationInfo[SKL.dataSource]) {
return await this.getDataFromDataSource(
getValueIfDefined(operationInfo[SKL.dataSource])!,
getIdFromNodeObjectIfDefined(operationInfo[SKL.dataSource] as string | ReferenceNodeObject)!,
verbConfig,
);
}
if (operationInfo[SKL.operationId]) {
Expand Down Expand Up @@ -516,6 +548,7 @@ export class SKLEngine {
private async performReturnValueMappingWithFrame(
returnValue: JSONObject,
mapping: MappingWithReturnValueMapping,
verbConfig?: VerbConfig,
verb?: Entity,
): Promise<NodeObject> {
return await this.performMapping(
Expand All @@ -525,12 +558,14 @@ export class SKLEngine {
...getValueIfDefined<JSONObject>(verb?.[SKL.returnValueFrame]),
...getValueIfDefined<JSONObject>(mapping[SKL.returnValueFrame]),
},
verbConfig,
);
}

private async performParameterMappingOnArgsIfDefined(
args: JSONObject,
mapping: Partial<MappingWithParameterMapping> | Partial<MappingWithParameterReference>,
verbConfig?: VerbConfig,
convertToJsonDeep = true,
): Promise<Record<string, any>> {
if (SKL.parameterReference in mapping) {
Expand All @@ -542,6 +577,7 @@ export class SKLEngine {
args,
(mapping as MappingWithParameterMapping)[SKL.parameterMapping]!,
getValueIfDefined(mapping[SKL.parameterMappingFrame]),
verbConfig,
);
return toJSON(mappedData, convertToJsonDeep);
}
Expand Down Expand Up @@ -586,10 +622,15 @@ export class SKLEngine {
private async executeNounMappingVerb(verb: Entity, args: JSONObject, verbConfig?: VerbConfig): Promise<NodeObject> {
const mapping = await this.findVerbNounMapping(verb['@id'], args.noun as string);
if (mapping[SKL.returnValueMapping]) {
return await this.performReturnValueMappingWithFrame(args, mapping as MappingWithReturnValueMapping, verb);
return await this.performReturnValueMappingWithFrame(
args,
mapping as MappingWithReturnValueMapping,
verbConfig,
verb,
);
}
const verbArgs = await this.performParameterMappingOnArgsIfDefined(args, mapping, false);
const verbId = await this.performVerbMappingWithArgs(args, mapping);
const verbArgs = await this.performParameterMappingOnArgsIfDefined(args, mapping, verbConfig, false);
const verbId = await this.performVerbMappingWithArgs(args, mapping, verbConfig);
const mappedVerb = (await this.findBy({ id: verbId })) as Verb;

this.globalCallbacks?.onVerbStart?.(verb['@id'], verbArgs);
Expand Down Expand Up @@ -617,11 +658,17 @@ export class SKLEngine {
private async performVerbMappingWithArgs(
args: JSONObject,
mapping: MappingWithVerbMapping,
verbConfig?: VerbConfig,
): Promise<string | undefined> {
if (mapping[SKL.verbId]) {
return getValueIfDefined<string>(mapping[SKL.verbId])!;
}
const verbInfoJsonLd = await this.performMapping(args, mapping[SKL.verbMapping] as NodeObject);
const verbInfoJsonLd = await this.performMapping(
args,
mapping[SKL.verbMapping] as NodeObject,
undefined,
verbConfig,
);
return getValueIfDefined<string>(verbInfoJsonLd[SKL.verbId])!;
}

Expand Down Expand Up @@ -693,6 +740,7 @@ export class SKLEngine {
securityCredentials: Entity,
integrationId: string,
account: Entity,
verbConfig?: VerbConfig,
): Promise<OpenApiClientConfiguration> {
const getOauthTokenVerb = (await this.findBy({ type: SKL.Verb, [RDFS.label]: 'getOauthTokens' })) as Verb;
const mapping = await this.findVerbIntegrationMapping(getOauthTokenVerb['@id'], integrationId);
Expand All @@ -702,17 +750,20 @@ export class SKLEngine {
jwtBearerOptions: getValueIfDefined<string>(securityCredentials[SKL.jwtBearerOptions])!,
},
mapping,
verbConfig,
);
const operationInfoJsonLd = await this.performOperationMappingWithArgs({}, mapping);
const operationInfoJsonLd = await this.performOperationMappingWithArgs({}, mapping, verbConfig);
const rawReturnValue = await this.performOperation(
operationInfoJsonLd,
operationArgs,
account,
securityCredentials,
verbConfig,
);
const mappedReturnValue = await this.performReturnValueMappingWithFrame(
rawReturnValue,
mapping as MappingWithReturnValueMapping,
verbConfig,
getOauthTokenVerb,
);
await this.assertVerbReturnValueMatchesReturnTypeSchema(mappedReturnValue, getOauthTokenVerb);
Expand Down Expand Up @@ -833,27 +884,34 @@ export class SKLEngine {
return this.axiosResponseAndParamsToOperationResponse(response, operationParameters);
}

private async getDataFromDataSource(dataSourceId: string): Promise<OperationResponse> {
private async getDataFromDataSource(dataSourceId: string, verbConfig?: VerbConfig): Promise<OperationResponse> {
const dataSource = await this.findBy({ id: dataSourceId });
if (dataSource['@type'] === SKL.JsonDataSource) {
const data = this.getDataFromJsonDataSource(dataSource);
const data = this.getDataFromJsonDataSource(dataSource, verbConfig);
return { data, operationParameters: {}};
}
throw new Error(`DataSource type ${dataSource['@type']} is not supported.`);
}

private getDataFromJsonDataSource(dataSource: NodeObject): JSONObject {
private getDataFromJsonDataSource(dataSource: NodeObject, verbConfig?: VerbConfig): JSONObject {
if (dataSource[SKL.source]) {
const sourceValue = getValueIfDefined<string>(dataSource[SKL.source])!;
return this.getJsonDataFromSource(sourceValue);
return this.getJsonDataFromSource(sourceValue, verbConfig);
}
return getValueIfDefined<JSONObject>(dataSource[SKL.data])!;
}

private getJsonDataFromSource(source: string): JSONObject {
if (this.inputFiles && source in this.inputFiles) {
const file = this.inputFiles[source];
return JSON.parse(file);
private getJsonDataFromSource(source: string, verbConfig?: VerbConfig): JSONObject {
const inputFiles = {
...this.inputFiles,
...verbConfig?.inputFiles,
};
if (inputFiles && source in inputFiles) {
const file = inputFiles[source];
if (typeof file === 'string') {
return JSON.parse(file);
}
return file;
}
// eslint-disable-next-line unicorn/expiring-todo-comments
// TODO add support for remote sources
Expand Down
4 changes: 4 additions & 0 deletions src/SklEngineOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ export type SklEngineOptions = SklEngineStorageOptions & {
* When true, disables validation of verb parameters and return values according to schemas
*/
readonly disableValidation?: boolean;
/**
* An object containing files keyed on their title that can be used in mappings.
*/
readonly inputFiles?: Record<string, string>;
};
4 changes: 0 additions & 4 deletions src/storage/BaseQueryAdapterOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ export interface BaseQueryAdapterOptions {
* Query Adapter type. This value is required.
*/
readonly type: QueryAdapterType;
/**
* An object containing files keyed on their title that can be used in mappings.
*/
readonly inputFiles?: Record<string, string>;
/**
* Whether to set Dublic Core created and modified timestamps on saved entities. Defaults to false.
*/
Expand Down
Loading