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

feat: enhance construct elementString #103

Merged
merged 8 commits into from
Sep 23, 2024
162 changes: 153 additions & 9 deletions packages/services/src/__tests__/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { fillArray, randomIntegerString, generateUUID, incrementQuality } from '../utils/helpers';

describe('first', () => {
it('should return an array with the same length as the first argument', () => {
const first = [1, 2, 3];
const second = [{ a: 1 }];
const result = fillArray(first, second);
expect(result.length).toBe(first.length);
});
import {
fillArray,
randomIntegerString,
generateUUID,
incrementQuality,
concatService,
constructIdentifierString,
} from '../utils/helpers';

describe('helpers', () => {
it('should return an array with the same length as the first argument', () => {
const first = [1, 2, 3];
const second = [{ a: 1 }];
Expand Down Expand Up @@ -43,3 +43,147 @@ describe('first', () => {
expect(result.quantity).toBe(2);
});
});

describe('concatService', () => {
it('should concatenate the values of the arguments passed to it', () => {
namhoang1604 marked this conversation as resolved.
Show resolved Hide resolved
const data = {
productIdentifier: [
{
scheme: 'https://id.gs1.org/gtin',
identifierValue: '05012345678900',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://id.gs1.org/gtin/05012345678900/binding',
},
},
],
batchIdentifier: [
{
scheme: 'https://Cherriesfarm.example.org/batch',
identifierValue: '2024001',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://Cherriesfarm.example.org/batch/2024-001/binding',
},
},
],
itemIdentifier: [
{
scheme: 'https://Cherriesfarm.example.org/item',
identifierValue: 'TRF240001',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://Cherriesfarm.example.org/item/TRF-24-0001/binding',
},
},
],
};
const args: any = [
{ type: 'text', value: '(01)' },
{ type: 'path', value: '/productIdentifier/0/identifierValue' },
{ type: 'text', value: '(10)' },
{ type: 'path', value: '/batchIdentifier/0/identifierValue' },
{ type: 'text', value: '(21)' },
{ type: 'path', value: '/itemIdentifier/0/identifierValue' },
];
const result = concatService(data, ...args);
expect(result).toBe('(01)05012345678900(10)2024001(21)TRF240001');
});
});

describe('constructIdentifierString', () => {
it('should return the identifier string, when identifierKeyPath is an object', () => {
namhoang1604 marked this conversation as resolved.
Show resolved Hide resolved
const data = {
productIdentifier: [
{
scheme: 'https://id.gs1.org/gtin',
identifierValue: '05012345678900',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://id.gs1.org/gtin/05012345678900/binding',
},
},
],
batchIdentifier: [
{
scheme: 'https://Cherriesfarm.example.org/batch',
identifierValue: '2024001',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://Cherriesfarm.example.org/batch/2024-001/binding',
},
},
],
itemIdentifier: [
{
scheme: 'https://Cherriesfarm.example.org/item',
identifierValue: 'TRF240001',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://Cherriesfarm.example.org/item/TRF-24-0001/binding',
},
},
],
};
const identifierKeyPath: any = {
function: 'concatService',
args: [
{ type: 'text', value: '(01)' },
{ type: 'path', value: '/productIdentifier/0/identifierValue' },
{ type: 'text', value: '(10)' },
{ type: 'path', value: '/batchIdentifier/0/identifierValue' },
{ type: 'text', value: '(21)' },
{ type: 'path', value: '/itemIdentifier/0/identifierValue' },
],
};
const result = constructIdentifierString(data, identifierKeyPath);
expect(result).toBe('(01)05012345678900(10)2024001(21)TRF240001');
});

it('should return the identifier string, when identifierKeyPath is a path string', () => {
const data = {
productIdentifier: [
{
scheme: 'https://id.gs1.org/gtin',
identifierValue: '05012345678900',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://id.gs1.org/gtin/05012345678900/binding',
},
},
],
batchIdentifier: [
{
scheme: 'https://Cherriesfarm.example.org/batch',
identifierValue: '2024001',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://Cherriesfarm.example.org/batch/2024-001/binding',
},
},
],
itemIdentifier: [
{
scheme: 'https://Cherriesfarm.example.org/item',
identifierValue: 'TRF240001',
binding: {
type: 'document',
assuranceLevel: '3rdParty',
reference: 'https://Cherriesfarm.example.org/item/TRF-24-0001/binding',
},
},
],
};
const identifierKeyPath = '/productIdentifier/0/identifierValue';
const result = constructIdentifierString(data, identifierKeyPath);
expect(result).toBe('05012345678900');
});
});
4 changes: 2 additions & 2 deletions packages/services/src/epcisEvents/aggregationEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getStorageServiceLink } from '../storage.service.js';
import { LinkType, getLinkResolverIdentifier, registerLinkResolver } from '../linkResolver.service.js';
import { IService } from '../types/IService.js';
import { ITraceabilityEvent, IAggregationEventContext } from './types.js';
import { generateUUID } from '../utils/helpers.js';
import { constructIdentifierString, generateUUID } from '../utils/helpers.js';
import { validateAggregationEventContext } from './validateContext.js';
import { EPCISBusinessStepCode, EPCISEventAction, EPCISEventDisposition, EPCISEventType } from '../types/epcis.js';
import JSONPointer from 'jsonpointer';
Expand All @@ -19,7 +19,7 @@ export const processAggregationEvent: IService = async (
}

const { vckit, epcisAggregationEvent, dlr, storage, identifierKeyPath } = context;
const parentIdentifier = JSONPointer.get(aggregationEvent.data, identifierKeyPath);
const parentIdentifier = constructIdentifierString(aggregationEvent.data, identifierKeyPath);
if (!parentIdentifier) {
throw new Error('Identifier not found');
}
Expand Down
4 changes: 2 additions & 2 deletions packages/services/src/epcisEvents/transactionEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IService } from '../types/index.js';
import { issueVC } from '../vckit.service.js';
import { ITraceabilityEvent, ITransactionEventContext } from './types.js';
import { getStorageServiceLink } from '../storage.service.js';
import { generateUUID } from '../utils/helpers.js';
import { constructIdentifierString, generateUUID } from '../utils/helpers.js';
import { LinkType, getLinkResolverIdentifier, registerLinkResolver } from '../linkResolver.service.js';
import { validateTransactionEventContext } from './validateContext.js';
import JSONPointer from 'jsonpointer';
Expand All @@ -19,7 +19,7 @@ export const processTransactionEvent: IService = async (
}

const { vckit, epcisTransactionEvent, dlr, storage, identifierKeyPath, localStorageParams } = context;
const transactionIdentifier = JSONPointer.get(transactionEvent.data, identifierKeyPath);
const transactionIdentifier = constructIdentifierString(transactionEvent.data, identifierKeyPath);
if (!transactionIdentifier) {
throw new Error('Identifier not found');
}
Expand Down
2 changes: 1 addition & 1 deletion packages/services/src/epcisEvents/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface IContext {
vckit: IVCKitContext;
dlr: IConfigDLR;
storage: StorageServiceConfig;
identifierKeyPath: string;
identifierKeyPath: string | any;
namhoang1604 marked this conversation as resolved.
Show resolved Hide resolved
dpp: IEntityIssue;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/services/src/processDPP.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { VerifiableCredential } from '@vckit/core-types';
import { IService } from './types/index.js';
import { IContext } from './epcisEvents/types.js';
import { generateUUID } from './utils/helpers.js';
import { constructIdentifierString, generateUUID } from './utils/helpers.js';

import { getStorageServiceLink } from './storage.service.js';
import { issueVC } from './vckit.service.js';
Expand All @@ -21,7 +21,7 @@ export const processDPP: IService = async (data: any, context: IContext): Promis
const validationResult = validateContextDPP(context);
if (!validationResult.ok) throw new Error(validationResult.value);

const objectIdentifier = JSONPointer.get(credentialSubject, context.identifierKeyPath);
const objectIdentifier = constructIdentifierString(credentialSubject, context.identifierKeyPath);
if (!objectIdentifier) throw new Error('Identifier not found');

const { identifier, qualifierPath } = getLinkResolverIdentifier(objectIdentifier);
Expand Down
53 changes: 53 additions & 0 deletions packages/services/src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,35 @@ export const getValueByPath = (data: any, path: string) => {
return JSONPointer.get(data, path);
};

enum ConcatArgumentType {
TEXT = 'text',
PATH = 'path',
}
type ConcatArgument = {
type: ConcatArgumentType;
value: string;
};

/**
* This function concatenates the values of the arguments passed to it.
* The arguments can be either a string or a path to a value in the data object.
* If the argument is a path, the value at that path in the data object is used.
*
* @param data - data object
* @param args - arguments to concatenate
* @returns
*/
export const concatService = (data: any, ...args: ConcatArgument[]): string => {
return args.reduce((acc, arg) => {
if (arg.type === ConcatArgumentType.TEXT) {
return acc.concat(arg.value);
} else if (arg.type === ConcatArgumentType.PATH) {
return acc.concat(JSONPointer.get(data, arg.value));
}
return acc;
}, '');
};

export interface IConstructObjectParameters {
mappingFields?: { sourcePath: string; destinationPath: string }[];
dummyFields?: { path: string; data: any }[];
Expand All @@ -86,6 +115,10 @@ export const genericHandlerFunctions = {
generateIdWithBatchLot,
};

export const supportedHandlerFunctions: { [key: string]: any } = {
concatService,
};

export interface ICurrentAndDependencies {
currentData?: any;
dependenciesValues?: any[];
Expand Down Expand Up @@ -257,3 +290,23 @@ export const constructObject = (

return data;
};

export const constructIdentifierString = (
data: any,
identifierKeyPath: string | { function: string; args: any },
): string => {
if (typeof identifierKeyPath === 'string') {
return JSONPointer.get(data, identifierKeyPath);
} else {
try {
const handlerFunction: any = supportedHandlerFunctions[identifierKeyPath.function];
if (!handlerFunction) {
throw new Error(`Handler function ${identifierKeyPath.function} not found`);
}
return handlerFunction(data, ...identifierKeyPath.args);
} catch (error: any) {
console.log(error);
throw new Error('Error constructing identifier string using handler function');
}
}
};
Loading