diff --git a/constants/index.ts b/constants/index.ts index 15191e8b..b599dcf5 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -223,9 +223,7 @@ export const TRV14ApiSequence = { INIT: 'init', ON_INIT: 'on_init', CONFIRM: 'confirm', - ON_CONFIRM: 'on_confirm', - STATUS: 'status', - ON_STATUS: 'on_status', + ON_CONFIRM: 'on_confirm', STATUS: 'status', ON_STATUS: 'on_status', SOFT_CANCEL: 'soft_cancel', SOFT_ON_CANCEL: 'soft_on_cancel', CANCEL: 'cancel', @@ -288,8 +286,14 @@ export const airlinesSequence = { ON_CONFIRM: 'on_confirm', CANCEL: 'cancel', ON_CANCEL: 'on_cancel', + STATUS: 'status', + ON_STATUS: 'on_status' +} +export const intercitySequence = { + SEARCH: 'search', + ON_SEARCH: 'on_search', + SELECT: 'select', } - export const mobilityFlow: any = { AIRLINES: 'airlines', METRO: 'metro', diff --git a/controller/validate/helper.ts b/controller/validate/helper.ts index 56047533..bd089571 100644 --- a/controller/validate/helper.ts +++ b/controller/validate/helper.ts @@ -11,7 +11,7 @@ import { validateLogsForFIS13 } from '../../shared/Actions/FIS13Actions' import { validateLogsForTRV13 } from '../../shared/Actions/TRV13Actions' import { getFis14Format, validateLogsForFIS14 } from '../../shared/Actions/FIS14Actions' import { validateLogsForTRV14 } from '../../shared/Actions/TRV14Actions' -import { validateLogsForAirline } from '../../shared/Actions/TRV12Action' +import { validateLogsForTRV12 } from '../../shared/Actions/TRV12Action' const createSignature = async ({ message }: { message: string }) => { const privateKey = process.env.SIGN_PRIVATE_KEY as string @@ -159,7 +159,7 @@ const validateMobility = async (domain: string, payload: string, version: string break case 'ONDC:TRV12': - response = validateLogsForAirline(payload, flow, version) + response = validateLogsForTRV12(payload, flow, version) if (_.isEmpty(response)) { success = true diff --git a/controller/validate/index.ts b/controller/validate/index.ts index bcea9979..0930ade8 100644 --- a/controller/validate/index.ts +++ b/controller/validate/index.ts @@ -48,6 +48,7 @@ const controller = { { const { response, success, message } = await helper.validateMobility(domain, payload, version, flow) result = { response, success, message } + } break @@ -77,7 +78,6 @@ const controller = { domain, reportTimestamp: new Date().toISOString(), } - const { signature, currentDate } = await helper.createSignature({ message: JSON.stringify(httpResponse) }) if (!success) diff --git a/schema/TRV-12/cancel.ts b/schema/TRV-12/Airlines/cancel.ts similarity index 100% rename from schema/TRV-12/cancel.ts rename to schema/TRV-12/Airlines/cancel.ts diff --git a/schema/TRV-12/confirm.ts b/schema/TRV-12/Airlines/confirm.ts similarity index 100% rename from schema/TRV-12/confirm.ts rename to schema/TRV-12/Airlines/confirm.ts diff --git a/schema/TRV-12/init.ts b/schema/TRV-12/Airlines/init.ts similarity index 100% rename from schema/TRV-12/init.ts rename to schema/TRV-12/Airlines/init.ts diff --git a/schema/TRV-12/on_cancel.ts b/schema/TRV-12/Airlines/on_cancel.ts similarity index 100% rename from schema/TRV-12/on_cancel.ts rename to schema/TRV-12/Airlines/on_cancel.ts diff --git a/schema/TRV-12/on_confirm.ts b/schema/TRV-12/Airlines/on_confirm.ts similarity index 100% rename from schema/TRV-12/on_confirm.ts rename to schema/TRV-12/Airlines/on_confirm.ts diff --git a/schema/TRV-12/on_init.ts b/schema/TRV-12/Airlines/on_init.ts similarity index 100% rename from schema/TRV-12/on_init.ts rename to schema/TRV-12/Airlines/on_init.ts diff --git a/schema/TRV-12/on_search.ts b/schema/TRV-12/Airlines/on_search.ts similarity index 100% rename from schema/TRV-12/on_search.ts rename to schema/TRV-12/Airlines/on_search.ts diff --git a/schema/TRV-12/on_select.ts b/schema/TRV-12/Airlines/on_select.ts similarity index 68% rename from schema/TRV-12/on_select.ts rename to schema/TRV-12/Airlines/on_select.ts index 9669e25a..181f7915 100644 --- a/schema/TRV-12/on_select.ts +++ b/schema/TRV-12/Airlines/on_select.ts @@ -1,4 +1,4 @@ -export const onSelectSchemaTRV_12 = { +export const onSelectSchemaTRV_12 = { type: 'object', required: ['context', 'message'], properties: { @@ -228,9 +228,103 @@ export const onSelectSchemaTRV_12 = { }, }, }, + quote: { + type: 'object', + properties: { + price: { + type: 'object', + required: ['value', 'currency'], + properties: { + value: { type: 'string' }, + currency: { type: 'string', enum: ['INR'] }, + }, + }, + breakup: { + type: 'array', + items: { + type: 'object', + required: ['title', 'price'], + properties: { + title: { type: 'string', enum: ['BASE_FARE', 'TAX', 'CONVENIENCE_FEE', 'SEAT_FARE', 'ADD_ONS', 'OTHER_CHARGES'] }, + item: { + type: 'object', + properties: { + id: { type: 'string' }, + quantity: { + type: 'object', + properties: { + selected: { + type: 'object', + properties: { + count: { type: 'integer' }, + }, + }, + }, + }, + price: { + type: 'object', + properties: { + currency: { type: 'string', enum: ['INR'] }, + value: { type: 'string' }, + }, + }, + add_ons: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string' }, + }, + }, + }, + tags: { + type: 'array', + items: { + type: 'object', + properties: { + descriptor: { + type: 'object', + properties: { + code: { type: 'string', enum: ['TAX', 'OTHER_CHARGES'] }, + name: { type: 'string' }, + }, + }, + list: { + type: 'array', + items: { + type: 'object', + properties: { + descriptor: { + type: 'object', + properties: { + name: { type: 'string' }, + code: { type: 'string' }, + }, + }, + value: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }, + }, + price: { + type: 'object', + properties: { + currency: { type: 'string', enum: ['INR'] }, + value: { type: 'string' }, + }, + }, + }, + }, + }, + }, + }, }, }, }, }, }, -} +}; diff --git a/schema/TRV-12/search.ts b/schema/TRV-12/Airlines/search.ts similarity index 100% rename from schema/TRV-12/search.ts rename to schema/TRV-12/Airlines/search.ts diff --git a/schema/TRV-12/Airlines/select.ts b/schema/TRV-12/Airlines/select.ts new file mode 100644 index 00000000..e6b52f84 --- /dev/null +++ b/schema/TRV-12/Airlines/select.ts @@ -0,0 +1,206 @@ +export const selectSchemaTRV_12 = { + type: 'object', + required: ['context', 'message'], + properties: { + context: { + type: 'object', + required: [ + 'location', + 'domain', + 'timestamp', + 'bap_id', + 'transaction_id', + 'message_id', + 'version', + 'action', + 'bap_uri', + 'bpp_id', + 'bpp_uri', + 'ttl', + ], + properties: { + location: { + type: 'object', + required: ['country', 'city'], + properties: { + country: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string', enum: ['IND'] }, + }, + }, + city: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string', pattern: '^std:\\d{3}$' }, + }, + }, + }, + }, + domain: { type: 'string', enum: ['ONDC:TRV12'] }, + timestamp: { type: 'string', format: 'date-time' }, + bap_id: { type: 'string' }, + transaction_id: { type: 'string', format: 'uuid' }, + message_id: { type: 'string', format: 'uuid' }, + version: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+$' }, + action: { type: 'string', enum: ['select'] }, + bap_uri: { type: 'string', format: 'uri' }, + bpp_id: { type: 'string' }, + bpp_uri: { type: 'string', format: 'uri' }, + ttl: { type: 'string', pattern: '^PT\\d+S$' }, + }, + }, + message: { + type: 'object', + required: ['order'], + properties: { + order: { + type: 'object', + required: ['provider', 'items', 'fulfillments'], + properties: { + provider: { + type: 'object', + required: ['id'], + properties: { + id: { type: 'string' }, + }, + }, + items: { + type: 'array', + items: { + type: 'object', + required: ['quantity'], + properties: { + id: { type: 'string' }, + parent_item_id: { type: 'string' }, + quantity: { + type: 'object', + required: ['selected'], + properties: { + selected: { + type: 'object', + required: ['count'], + properties: { + count: { type: 'integer', minimum: 1 }, + }, + }, + }, + }, + add_ons: { + type: 'array', + items: { + type: 'object', + required: ['id', 'quantity'], + properties: { + id: { type: 'string' }, + quantity: { + type: 'object', + required: ['selected'], + properties: { + selected: { + type: 'object', + required: ['count'], + properties: { + count: { type: 'integer', minimum: 1 }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + fulfillments: { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": ["id"] + } + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["INFO", "SEAT_GRID"] + }, + "name": { + "type": "string" + } + }, + "required": ["code"] + }, + "display": { + "type": "boolean" + }, + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": ["code"] + }, + "value": { + "type": "string" + } + }, + "required": ["descriptor"] + } + } + }, + "required": ["descriptor"] + } + }, + "vehicle": { + "type": "object", + "properties": { + "category": { + "type": "string", + "enum": ["AIRLINE"] + }, + "code": { + "type": "string" + } + }, + "required": ["category", "code"] + } + }, + "required": ["id"] + } + } + , + }, + }, + }, + }, + }, +}; diff --git a/schema/TRV-12/Intercity/onSearch.ts b/schema/TRV-12/Intercity/onSearch.ts new file mode 100644 index 00000000..db4fc218 --- /dev/null +++ b/schema/TRV-12/Intercity/onSearch.ts @@ -0,0 +1,205 @@ +export const onSearchSchemaTRV12BUS = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "country": { + "type": "object", + "properties": { + "code": { "type": "string" } + }, + "required": ["code"] + }, + "city": { + "type": "object", + "properties": { + "code": { "type": "string" } + }, + "required": ["code"] + } + }, + "required": ["country", "city"] + }, + "domain": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" }, + "bap_id": { "type": "string" }, + "bap_uri": { "type": "string" }, + "bpp_id": { "type": "string" }, + "bpp_uri": { "type": "string" }, + "transaction_id": { "type": "string" }, + "message_id": { "type": "string" }, + "version": { "type": "string" }, + "action": { "type": "string", "enum": ["on_search"] }, + "ttl": { "type": "string" } + }, + "required": [ + "location", + "domain", + "timestamp", + "bap_id", + "bap_uri", + "bpp_id", + "bpp_uri", + "transaction_id", + "message_id", + "version", + "action", + "ttl" + ] + }, + "message": { + "type": "object", + "properties": { + "catalog": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "url": { "type": "string" } + }, + "required": ["url"] + } + } + }, + "required": ["name", "images"] + }, + "providers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "descriptor": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "images": { + "type": "array", + "items": { + "type": "object", + "properties": { + "url": { "type": "string" } + }, + "required": ["url"] + } + } + }, + "required": ["name", "images"] + }, + "fulfillments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "type": { "type": "string" }, + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { "type": "string" }, + "location": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "code": { "type": "string" } + }, + "required": ["name", "code"] + }, + "gps": { "type": "string" } + }, + "required": ["descriptor", "gps"] + } + }, + "required": ["type", "location"] + } + }, + "vehicle": { + "type": "object", + "properties": { + "category": { "type": "string" }, + "variant": { "type": "string" }, + "capacity": { "type": "integer" }, + "wheels_count": { "type": "string" }, + "energy_type": { "type": "string" } + }, + "required": ["category", "variant", "capacity", "wheels_count", "energy_type"] + } + }, + "required": ["id", "type", "stops", "vehicle"] + } + }, + "payments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "collected_by": { "type": "string" }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { "type": "string" } + }, + "required": ["code"] + }, + "display": { "type": "boolean" }, + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { "type": "string" } + }, + "required": ["code"] + }, + "value": { "type": "string" } + }, + "required": ["descriptor", "value"] + } + } + }, + "required": ["descriptor", "display", "list"] + } + } + }, + "required": ["collected_by", "tags"] + } + } + }, + "required": ["id", "descriptor", "fulfillments", "payments"] + } + } + }, + "required": ["descriptor", "providers"] + } + }, + "required": ["catalog"] + } + }, + "required": ["context", "message"] + } + \ No newline at end of file diff --git a/schema/TRV-12/Intercity/search.ts b/schema/TRV-12/Intercity/search.ts new file mode 100644 index 00000000..f21c5c00 --- /dev/null +++ b/schema/TRV-12/Intercity/search.ts @@ -0,0 +1,201 @@ +export const searchSchemaTRV12BUS = { + type: 'object', + required: ['context', 'message'], + properties: { + context: { + type: 'object', + required: [ + 'location', + 'domain', + 'timestamp', + 'bap_id', + 'transaction_id', + 'message_id', + 'version', + 'action', + 'bap_uri', + 'ttl' + ], + properties: { + location: { + type: 'object', + required: ['country', 'city'], + properties: { + country: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string' } + } + }, + city: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string' } + } + } + } + }, + domain: { type: 'string', enum: ['ONDC:TRV12'] }, + timestamp: { type: 'string', format: 'date-time' }, + bap_id: { type: 'string' }, + transaction_id: { type: 'string' }, + message_id: { type: 'string' }, + version: { type: 'string' }, + action: { type: 'string', enum: ['search'] }, + bap_uri: { type: 'string', format: 'uri' }, + ttl: { type: 'string', pattern: '^PT\\d+S$' } + } + }, + message: { + type: 'object', + required: ['intent'], + properties: { + intent: { + type: 'object', + required: ['fulfillment', 'payment'], + properties: { + category: { + type: 'object', + required: ['descriptor'], + properties: { + descriptor: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string', enum: ['ECONOMY'] } + } + } + } + }, + fulfillment: { + type: 'object', + required: ['stops', 'vehicle'], + properties: { + stops: { + type: 'array', + minItems: 1, + items: { + type: 'object', + required: ['type', 'location'], + properties: { + type: { type: 'string' }, + location: { + type: 'object', + required: ['descriptor'], + properties: { + descriptor: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string' } + } + } + } + }, + time: { + type: 'object', + properties: { + label: { type: 'string' }, + timestamp: { type: 'string', format: 'date-time' } + } + } + } + } + }, + vehicle: { + type: 'object', + required: ['category'], + properties: { + category: { type: 'string', enum: ['BUS', 'AIRLINE'] }, + variant: { type: 'string' }, + capacity: { type: 'integer' }, + wheels_count: { type: 'string' }, + energy_type: { type: 'string' } + } + } + } + }, + provider: { + type: 'object', + properties: { + items: { + type: 'array', + items: { + type: 'object', + required: ['descriptor', 'quantity'], + properties: { + descriptor: { + type: 'object', + required: ['name', 'code'], + properties: { + name: { type: 'string' }, + code: { type: 'string', enum: ['ADULT_TICKET', 'CHILD_TICKET'] } + } + }, + quantity: { + type: 'object', + required: ['selected'], + properties: { + selected: { + type: 'object', + required: ['count'], + properties: { + count: { type: 'integer' } + } + } + } + } + } + } + } + } + }, + payment: { + type: 'object', + required: ['tags'], + properties: { + tags: { + type: 'array', + items: { + type: 'object', + required: ['descriptor', 'display', 'list'], + properties: { + descriptor: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string', enum: ['BUYER_FINDER_FEES', 'SETTLEMENT_TERMS'] } + } + }, + display: { type: 'boolean' }, + list: { + type: 'array', + items: { + type: 'object', + required: ['descriptor', 'value'], + properties: { + descriptor: { + type: 'object', + required: ['code'], + properties: { + code: { type: 'string' } + } + }, + value: { type: 'string' } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + \ No newline at end of file diff --git a/schema/TRV-12/Intercity/select.ts b/schema/TRV-12/Intercity/select.ts new file mode 100644 index 00000000..c9ec0fa2 --- /dev/null +++ b/schema/TRV-12/Intercity/select.ts @@ -0,0 +1,121 @@ +export const selectSchemaTRV12BUS = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "country": { + "type": "object", + "properties": { + "code": { "type": "string" } + }, + "required": ["code"] + }, + "city": { + "type": "object", + "properties": { + "code": { "type": "string" } + }, + "required": ["code"] + } + }, + "required": ["country", "city"] + }, + "domain": { "type": "string" }, + "timestamp": { "type": "string", "format": "date-time" }, + "bap_id": { "type": "string" }, + "bap_uri": { "type": "string" }, + "bpp_id": { "type": "string" }, + "bpp_uri": { "type": "string" }, + "transaction_id": { "type": "string" }, + "message_id": { "type": "string" }, + "version": { "type": "string" }, + "action": { "type": "string", "enum": ["select"] }, + "ttl": { "type": "string" } + }, + "required": [ + "location", + "domain", + "timestamp", + "bap_id", + "bap_uri", + "bpp_id", + "bpp_uri", + "transaction_id", + "message_id", + "version", + "action", + "ttl" + ] + }, + "message": { + "type": "object", + "properties": { + "order": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "quantity": { + "type": "object", + "properties": { + "selected": { + "type": "object", + "properties": { + "count": { "type": "integer" } + }, + "required": ["count"] + } + }, + "required": ["selected"] + } + }, + "required": ["id", "quantity"] + } + }, + "fulfillments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "type": { "type": "string" } + }, + "required": ["id", "type"] + } + } + }, + "required": ["id", "stops"] + } + }, + "provider": { + "type": "object", + "properties": { + "id": { "type": "string" } + }, + "required": ["id"] + } + }, + "required": ["items", "fulfillments", "provider"] + } + }, + "required": ["order"] + } + }, + "required": ["context", "message"] + } + \ No newline at end of file diff --git a/schema/TRV-12/select.ts b/schema/TRV-12/select.ts deleted file mode 100644 index f7dd8b99..00000000 --- a/schema/TRV-12/select.ts +++ /dev/null @@ -1,166 +0,0 @@ -export const selectSchemaTRV_12 = { - type: 'object', - required: ['context', 'message'], - properties: { - context: { - type: 'object', - required: [ - 'location', - 'domain', - 'timestamp', - 'bap_id', - 'transaction_id', - 'message_id', - 'version', - 'action', - 'bap_uri', - 'bpp_id', - 'bpp_uri', - 'ttl', - ], - properties: { - location: { - type: 'object', - required: ['country', 'city'], - properties: { - country: { - type: 'object', - required: ['code'], - properties: { - code: { type: 'string', enum: ['IND'] }, - }, - }, - city: { - type: 'object', - required: ['code'], - properties: { - code: { type: 'string', pattern: '^std:\\d{3}$' }, - }, - }, - }, - }, - domain: { type: 'string', enum: ['ONDC:TRV12'] }, - timestamp: { type: 'string', format: 'date-time' }, - bap_id: { type: 'string' }, - transaction_id: { type: 'string', format: 'uuid' }, - message_id: { type: 'string', format: 'uuid' }, - version: { type: 'string', pattern: '^\\d+\\.\\d+\\.\\d+$' }, - action: { type: 'string', enum: ['select'] }, - bap_uri: { type: 'string', format: 'uri' }, - bpp_id: { type: 'string' }, - bpp_uri: { type: 'string', format: 'uri' }, - ttl: { type: 'string', pattern: '^PT\\d+S$' }, - }, - }, - message: { - type: 'object', - required: ['order'], - properties: { - order: { - type: 'object', - required: ['provider', 'items', 'fulfillments'], - properties: { - provider: { - type: 'object', - required: ['id'], - properties: { - id: { type: 'string' }, - }, - }, - items: { - type: 'array', - items: { - oneOf: [ - { - type: 'object', - required: ['id', 'quantity'], - properties: { - id: { type: 'string' }, - quantity: { - type: 'object', - required: ['selected'], - properties: { - selected: { - type: 'object', - required: ['count'], - properties: { - count: { type: 'integer', minimum: 1 }, - }, - }, - }, - }, - }, - }, - { - type: 'object', - required: ['parent_item_id', 'quantity'], - properties: { - parent_item_id: { type: 'string' }, - quantity: { - type: 'object', - required: ['selected'], - properties: { - selected: { - type: 'object', - required: ['count'], - properties: { - count: { type: 'integer', minimum: 1 }, - }, - }, - }, - }, - add_ons: { - type: 'array', - items: { - type: 'object', - required: ['id', 'quantity'], - properties: { - id: { type: 'string' }, - quantity: { - type: 'object', - required: ['selected'], - properties: { - selected: { - type: 'object', - required: ['count'], - properties: { - count: { type: 'integer', minimum: 1 }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - ], - }, - }, - fulfillments: { - type: 'array', - items: { - type: 'object', - required: ['id', 'stops'], - properties: { - id: { type: 'string' }, - stops: { - type: 'array', - minItems: 2, - items: { - type: 'object', - required: ['id'], - properties: { - id: { type: 'string' }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, -} diff --git a/shared/Actions/TRV12Action.ts b/shared/Actions/TRV12Action.ts index 4bdbf8c1..96478756 100644 --- a/shared/Actions/TRV12Action.ts +++ b/shared/Actions/TRV12Action.ts @@ -1,7 +1,7 @@ import _ from 'lodash' import { dropDB } from '../dao' import { logger } from '../logger' -import { airlinesSequence } from '../../constants' +import { airlinesSequence, intercitySequence } from '../../constants' import { search } from '../../utils/mobility/TRV12/search' import { checkOnSearch } from '../../utils/mobility/TRV12/onSearch' import { checkSelect } from '../../utils/mobility/TRV12/select' @@ -12,12 +12,16 @@ import { checkConfirm } from '../../utils/mobility/TRV12/confirm' import { checkOnConfirm } from '../../utils/mobility/TRV12/onConfirm' import { checkCancelPayload } from '../../utils/mobility/TRV12/cancel' import { checkOnCancelPayload } from '../../utils/mobility/TRV12/onCancel' +import { checkStatusPayload } from '../../utils/mobility/TRV12/status' +import { checkOnStatusPayload } from '../../utils/mobility/TRV12/onStatus' -export function validateLogsForAirline(data: any, flowName: string, version: string) { +export function validateLogsForTRV12(data: any, flowName: string, version: string) { const msgIdSet = new Set() let logReport: any = {} - const [first, ...rest] = flowName.split('_') - const flow = { flow: first, flowSet: rest.join('_') } + console.log('flowname: ', flowName) + const [first, ...rest] = flowName.split('-') + const flow = { flow: first.trim(), flowSet: rest.join('-') } + const realFlow = flow.flow.substring(0, flow.flow.length - 1).toUpperCase() try { dropDB() } catch (error) { @@ -25,81 +29,119 @@ export function validateLogsForAirline(data: any, flowName: string, version: str } try { - if (data[airlinesSequence?.SEARCH]) { - const searchResp = search(data[airlinesSequence?.SEARCH], msgIdSet, flow) - if (!_.isEmpty(searchResp)) { - logReport = { ...logReport, [airlinesSequence.SEARCH]: searchResp } - } - } + switch (realFlow) { + case 'AIRLINE': + if (data[airlinesSequence?.SEARCH]) { + const searchResp = search(data[airlinesSequence?.SEARCH], msgIdSet, flow) + if (!_.isEmpty(searchResp)) { + logReport = { ...logReport, [airlinesSequence.SEARCH]: searchResp } + } + } - if (data[airlinesSequence.ON_SEARCH]) { - const onSearchResp = checkOnSearch(data[airlinesSequence.ON_SEARCH], msgIdSet, false, flow) - if (!_.isEmpty(onSearchResp)) { - logReport = { ...logReport, [airlinesSequence.ON_SEARCH]: onSearchResp } - } - } + if (data[airlinesSequence.ON_SEARCH]) { + const onSearchResp = checkOnSearch(data[airlinesSequence.ON_SEARCH], msgIdSet, false, flow) + if (!_.isEmpty(onSearchResp)) { + logReport = { ...logReport, [airlinesSequence.ON_SEARCH]: onSearchResp } + } + } - if (data[airlinesSequence.ON_SEARCH]) { - const selectresp = checkOnSearch(data[airlinesSequence.ON_SEARCH], msgIdSet, true, flow) - if (!_.isEmpty(selectresp)) { - logReport = { ...logReport, [airlinesSequence.ON_SEARCH]: selectresp } - } - } + if (data[airlinesSequence.ON_SEARCH]) { + const selectresp = checkOnSearch(data[airlinesSequence.ON_SEARCH], msgIdSet, true, flow) + if (!_.isEmpty(selectresp)) { + logReport = { ...logReport, [airlinesSequence.ON_SEARCH]: selectresp } + } + } - if (data[airlinesSequence.SELECT1]) { - const onSelectResp = checkSelect(data[airlinesSequence.SELECT1], msgIdSet) - if (!_.isEmpty(onSelectResp)) { - logReport = { ...logReport, [airlinesSequence.SELECT1]: onSelectResp } - } - } + if (data[airlinesSequence.SELECT1]) { + const onSelectResp = checkSelect(data[airlinesSequence.SELECT1], msgIdSet, flow) + if (!_.isEmpty(onSelectResp)) { + logReport = { ...logReport, [airlinesSequence.SELECT1]: onSelectResp } + } + } - if (data[airlinesSequence.ON_SELECT1]) { - const onSelectResp = checkOnSelect(data[airlinesSequence.ON_SELECT1], msgIdSet, flow, version) - if (!_.isEmpty(onSelectResp)) { - logReport = { ...logReport, [airlinesSequence.ON_SELECT1]: onSelectResp } - } - } + if (data[airlinesSequence.ON_SELECT1]) { + const onSelectResp = checkOnSelect(data[airlinesSequence.ON_SELECT1], msgIdSet, flow, version) + if (!_.isEmpty(onSelectResp)) { + logReport = { ...logReport, [airlinesSequence.ON_SELECT1]: onSelectResp } + } + } - if (data[airlinesSequence.INIT]) { - const initResp = checkInit(data[airlinesSequence.INIT], msgIdSet) - if (!_.isEmpty(initResp)) { - logReport = { ...logReport, [airlinesSequence.INIT]: initResp } - } - } + if (data[airlinesSequence.INIT]) { + const initResp = checkInit(data[airlinesSequence.INIT], msgIdSet) + if (!_.isEmpty(initResp)) { + logReport = { ...logReport, [airlinesSequence.INIT]: initResp } + } + } - if (data[airlinesSequence.ON_INIT]) { - const onInitResp = checkOnInit(data[airlinesSequence.ON_INIT], msgIdSet, flow, version) - if (!_.isEmpty(onInitResp)) { - logReport = { ...logReport, [airlinesSequence.ON_INIT]: onInitResp } - } - } + if (data[airlinesSequence.ON_INIT]) { + const onInitResp = checkOnInit(data[airlinesSequence.ON_INIT], msgIdSet, flow, version) + if (!_.isEmpty(onInitResp)) { + logReport = { ...logReport, [airlinesSequence.ON_INIT]: onInitResp } + } + } - if (data[airlinesSequence.CONFIRM]) { - const confirmResp = checkConfirm(data[airlinesSequence.CONFIRM], msgIdSet) - if (!_.isEmpty(confirmResp)) { - logReport = { ...logReport, [airlinesSequence.CONFIRM]: confirmResp } - } - } + if (data[airlinesSequence.CONFIRM]) { + const confirmResp = checkConfirm(data[airlinesSequence.CONFIRM], msgIdSet) + if (!_.isEmpty(confirmResp)) { + logReport = { ...logReport, [airlinesSequence.CONFIRM]: confirmResp } + } + } - if (data[airlinesSequence.ON_CONFIRM]) { - const onConfirmResp = checkOnConfirm(data[airlinesSequence.ON_CONFIRM], msgIdSet, flow, version) - if (!_.isEmpty(onConfirmResp)) { - logReport = { ...logReport, [airlinesSequence.ON_CONFIRM]: onConfirmResp } - } - } + if (data[airlinesSequence.ON_CONFIRM]) { + const onConfirmResp = checkOnConfirm(data[airlinesSequence.ON_CONFIRM], msgIdSet, flow, version) + if (!_.isEmpty(onConfirmResp)) { + logReport = { ...logReport, [airlinesSequence.ON_CONFIRM]: onConfirmResp } + } + } - if (data[airlinesSequence.CANCEL]) { - const cancelResp = checkCancelPayload(data[airlinesSequence.CANCEL], msgIdSet, false) - if (!_.isEmpty(cancelResp)) { - logReport = { ...logReport, [airlinesSequence.CANCEL]: cancelResp } - } - } + if (data[airlinesSequence.CANCEL]) { + const cancelResp = checkCancelPayload(data[airlinesSequence.CANCEL], msgIdSet, false) + if (!_.isEmpty(cancelResp)) { + logReport = { ...logReport, [airlinesSequence.CANCEL]: cancelResp } + } + } + + if (data[airlinesSequence.ON_CANCEL]) { + const onCancelResp = checkOnCancelPayload(data[airlinesSequence.ON_CANCEL], msgIdSet, flow, false) + if (!_.isEmpty(onCancelResp)) { + logReport = { ...logReport, [airlinesSequence.ON_CANCEL]: onCancelResp } + } + } + if (data[airlinesSequence.STATUS]) { + const statusResp = checkStatusPayload(data[airlinesSequence.STATUS], msgIdSet) + if (!_.isEmpty(statusResp)) { + logReport = { ...logReport, [airlinesSequence.STATUS]: statusResp } + } + } + if (data[airlinesSequence.ON_STATUS]) { + const onStatusResp = checkOnStatusPayload(data[airlinesSequence.ON_STATUS], msgIdSet) + if (!_.isEmpty(onStatusResp)) { + logReport = { ...logReport, [airlinesSequence.ON_STATUS]: onStatusResp } + } + } + + break + case 'BUS': + if (data[intercitySequence?.SEARCH]) { + const searchResp = search(data[intercitySequence?.SEARCH], msgIdSet, flow) + if (!_.isEmpty(searchResp)) { + logReport = { ...logReport, [intercitySequence.SEARCH]: searchResp } + } + } + if (data[intercitySequence.ON_SEARCH]) { + const onSearchResp = checkOnSearch(data[intercitySequence.ON_SEARCH], msgIdSet, false, flow) + if (!_.isEmpty(onSearchResp)) { + logReport = { ...logReport, [intercitySequence.ON_SEARCH]: onSearchResp } + } + } + if (data[intercitySequence.SELECT]) { + const selectResp = checkSelect(data[intercitySequence.SELECT], msgIdSet, flow) + if (!_.isEmpty(selectResp)) { + logReport = { ...logReport, [intercitySequence.SELECT]: selectResp } + } + } - if (data[airlinesSequence.ON_CANCEL]) { - const onCancelResp = checkOnCancelPayload(data[airlinesSequence.ON_CANCEL], msgIdSet, flow, false) - if (!_.isEmpty(onCancelResp)) { - logReport = { ...logReport, [airlinesSequence.ON_CANCEL]: onCancelResp } - } + break } logger.info(logReport, 'Report Generated Successfully!!') diff --git a/shared/Actions/metroActions.ts b/shared/Actions/metroActions.ts index 7efeb5dc..0aed42e5 100644 --- a/shared/Actions/metroActions.ts +++ b/shared/Actions/metroActions.ts @@ -27,6 +27,7 @@ export function validateLogsForMetro(data: any, flowName: string, version: strin } try { + if (data[metroSequence.SEARCH1]) { const searchResp = search(data[metroSequence.SEARCH1], msgIdSet, false, flow) if (!_.isEmpty(searchResp)) { diff --git a/shared/schemaValidator.ts b/shared/schemaValidator.ts index bf496340..427c4da1 100644 --- a/shared/schemaValidator.ts +++ b/shared/schemaValidator.ts @@ -67,16 +67,19 @@ import onSearch1SchemaTRV14 from '../schema/TRV-14/onSearch1' import onSearch2SchemaTRV14 from '../schema/TRV-14/onSearch2' import newIssueSchema from '../schema/Igm/2.0.0/issue' import newOnIssueSchema from '../schema/Igm/2.0.0/on_issue' -import { searchSchemaTRV_12 } from '../schema/TRV-12/search' -import { onSearchSchemaTRV_12 } from '../schema/TRV-12/on_search' -import { onSelectSchemaTRV_12 } from '../schema/TRV-12/on_select' -import { selectSchemaTRV_12 } from '../schema/TRV-12/select' -import { initSchemaTRV_12 } from '../schema/TRV-12/init' -import { onInitSchemaTRV_12 } from '../schema/TRV-12/on_init' -import { confirmSchemaTRV_12 } from '../schema/TRV-12/confirm' -import { cancelSchemaTRV_12 } from '../schema/TRV-12/cancel' -import { onConfirmSchemaTRV_12 } from '../schema/TRV-12/on_confirm' -import { onCancelSchemaTRV_12 } from '../schema/TRV-12/on_cancel' +import { searchSchemaTRV_12 } from '../schema/TRV-12/Airlines/search' +import { onSearchSchemaTRV_12 } from '../schema/TRV-12/Airlines/on_search' +import { onSelectSchemaTRV_12 } from '../schema/TRV-12/Airlines/on_select' +import { selectSchemaTRV_12 } from '../schema/TRV-12/Airlines/select' +import { initSchemaTRV_12 } from '../schema/TRV-12/Airlines/init' +import { onInitSchemaTRV_12 } from '../schema/TRV-12/Airlines/on_init' +import { confirmSchemaTRV_12 } from '../schema/TRV-12/Airlines/confirm' +import { cancelSchemaTRV_12 } from '../schema/TRV-12/Airlines/cancel' +import { onConfirmSchemaTRV_12 } from '../schema/TRV-12/Airlines/on_confirm' +import { onCancelSchemaTRV_12 } from '../schema/TRV-12/Airlines/on_cancel' +import { searchSchemaTRV12BUS } from '../schema/TRV-12/Intercity/search' +import { onSearchSchemaTRV12BUS } from '../schema/TRV-12/Intercity/onSearch' +import { selectSchemaTRV12BUS } from '../schema/TRV-12/Intercity/select' const ajv = new Ajv({ allErrors: true, @@ -1100,15 +1103,31 @@ const validate_schema_search_TRV12_for_json = (data: any) => { return formatted_error(error_list) } +const validate_schema_search_TRV12BUS_for_json = (data: any) => { + const error_list = validate_schema(data, searchSchemaTRV12BUS) + return formatted_error(error_list) +} + const validate_schema_on_search_TRV12_for_json = (data: any) => { const error_list = validate_schema(data, onSearchSchemaTRV_12) return formatted_error(error_list) } +const validate_schema_on_search_TRV12BUS_for_json = (data: any) => { + const error_list = validate_schema(data, onSearchSchemaTRV12BUS) + return formatted_error(error_list) +} + const validate_schema_select_TRV12_for_json = (data: any) => { const error_list = validate_schema(data, selectSchemaTRV_12) return formatted_error(error_list) } +const validate_schema_select_TRV12BUS_for_json = (data: any) => { + const error_list = validate_schema(data, selectSchemaTRV12BUS) + return formatted_error(error_list) +} + + const validate_schema_on_select_TRV12_for_json = (data: any) => { const error_list = validate_schema(data, onSelectSchemaTRV_12) @@ -1501,6 +1520,11 @@ export default { validate_schema_cancel_TRV12_for_json, validate_schema_on_cancel_TRV12_for_json, + // TRV12-BUS + validate_schema_search_TRV12BUS_for_json, + validate_schema_on_search_TRV12BUS_for_json, + validate_schema_select_TRV12BUS_for_json, + ...TRVValidator, ...FISValidator, } diff --git a/utils/index.ts b/utils/index.ts index 2608110e..f2b950bc 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -216,6 +216,7 @@ const validate_schema_for_retail_json = (vertical: string, api: string, data: an console.log(`+++++++++ validate_schema_${api}_${vertical}_for_json`) const res = (schemaValidator as any)[`validate_schema_${api}_${vertical}_for_json`](data) + return res } @@ -225,6 +226,7 @@ export const validateSchema = (domain: string, api: string, data: any) => { const errObj: any = {} const schmaVldtr = validate_schema_for_retail_json(domain, api, data) + console.log('Schema Response: ', schmaVldtr) const datavld = schmaVldtr if (datavld.status === 'fail') { @@ -358,6 +360,7 @@ export const hasProperty = (object: any, propetyName: string) => { } export const isObjectEmpty = (obj: any) => { + console.log('Received Object Size: ', Object.keys(obj).length) return Object.keys(obj).length === 0 } diff --git a/utils/metro/enum.ts b/utils/metro/enum.ts index 0506925a..ce6c29e0 100644 --- a/utils/metro/enum.ts +++ b/utils/metro/enum.ts @@ -1,3 +1,3 @@ export const AUTHORIZATION_TYPE = ['QR'] export const AUTHORIZATION_STATUS = ['UNCLAIMED', 'CLAIMED'] -export const VALID_DESCRIPTOR_CODES = ['SJT', 'RJT', 'PASS', 'SFSJT'] +export const VALID_DESCRIPTOR_CODES = ['ADULT_TICKET', 'CHILD_TICKET'] diff --git a/utils/metro/mobilityChecks.ts b/utils/metro/mobilityChecks.ts index a1e28219..22c50250 100644 --- a/utils/metro/mobilityChecks.ts +++ b/utils/metro/mobilityChecks.ts @@ -14,6 +14,7 @@ export const validateContext = ( const errorObj: any = {} const contextRes: any = checkMobilityContext(context, curentCall) + if (!contextRes?.valid) { Object.assign(errorObj, contextRes.ERRORS) diff --git a/utils/metro/validate/helper.ts b/utils/metro/validate/helper.ts index d0c44e9a..a352a2c9 100644 --- a/utils/metro/validate/helper.ts +++ b/utils/metro/validate/helper.ts @@ -387,4 +387,6 @@ export function validateFulfillmentV2_0( export function validateDomain(domainName: string) { return domainName === METRODOMAIN.METRO ? true : false } + + \ No newline at end of file diff --git a/utils/mobility/TRV12/functions/helper.ts b/utils/mobility/TRV12/functions/helper.ts index dd204e21..78b420d9 100644 --- a/utils/mobility/TRV12/functions/helper.ts +++ b/utils/mobility/TRV12/functions/helper.ts @@ -1,27 +1,539 @@ +import { validatePaymentTags } from '../../tags' +import constants, { metroSequence } from '../../../../constants' +import { logger } from '../../../../shared/logger' +import { getValue, setValue } from '../../../../shared/dao' +import _, { isEmpty, isNil } from 'lodash' +// import { METRODOMAIN } from './functions/constant' + +export function checkItemQuantity(quantity: { [key: string]: any }, i: number, j: number) { + const errorObj: any = {} + if (!quantity) { + errorObj[`prvdr${i}item${j}_quantity`] = `Quantity is missing in /providers[${i}]/items[${j}]` + return errorObj + } + + if (!quantity?.selected?.count) { + errorObj[`prvdr${i}item${j}_quantity_selected`] = + `attribute selected.count is missing in /providers[${i}]/items[${j}]` + } + + return errorObj +} + +export function checkItemTime(time: { [key: string]: any }, i: number, j: number) { + const errorObj: any = {} + if (!time) { + errorObj['time'] = `time is missing in /providers[${i}]/items[${j}]` + return errorObj + } + + if (!time?.duration) { + errorObj[`prvdr${i}item${j}time.duration`] = `duration is missing in /providers[${i}]/items[${j}].time` + } + + if (!time?.label) { + errorObj[`prvdr${i}item${j}time.label`] = `label is missing in /providers[${i}]/items[${j}].time` + } else if (time?.label !== 'JOURNEY_TIME') + errorObj[`prvdr${i}item${j}time.label`] = + `label should be equal to 'JOURNEY_TIME' at /providers[${i}]/items[${j}].time` + + // if (!time?.timestamp) { + // errorObj[`prvdr${i}item${j}time.timestamp`] = `timestamp is missing in /providers[${i}]/items[${j}].time` + // } + + return errorObj +} + +export function checkItemPrice(price: { [key: string]: any }, i: number, j: number) { + const errorObj: any = {} + if (!price) { + errorObj['price'] = `price is missing in /providers[${i}]/items[${j}]` + return errorObj + } + + if (!price?.value) { + errorObj[`prvdr${i}item${j}price.value`] = `value is missing in /providers[${i}]/items[${j}].price` + } + + if (!price?.currency) { + errorObj[`prvdr${i}item${j}price.currency`] = `currency is missing in /providers[${i}]/items[${j}].price` + } else if (price?.currency.toUpperCase() !== 'INR') + errorObj[`prvdr${i}item${j}price.currency`] = + `currency should be equal to 'INR' at /providers[${i}]/items[${j}].price` + + return errorObj +} + +export function checkRefIds(refIds: string[], i: number, j: number, storedIds: any, refName: string) { + const errorObj: any = {} + if (_.isEmpty(refIds)) { + errorObj[`${refName}_ids`] = `${refName}_ids is missing or empty at /providers[${i}]/items[${j}]` + return errorObj + } + + refIds?.map((id: string) => { + if (!storedIds.includes(id)) + errorObj[`${refName}_ids`] = + `id:${id} doesn't match with the id's passed in ${refName}, at /providers[${i}]/items[${j}]` + }) + + return errorObj +} + +export function checkPayment(payments: any, i: number, action: string) { + const errorObj: any = {} + if (!payments || payments.length === 0) { + errorObj[`provider_${i}_payments`] = `Payments are missing for provider ${i}` + } else { + try { + logger.info(`Validating payments object for /${constants.ON_SEARCH}`) + payments?.map((payment: any, i: number) => { + const collectedBy = payment?.collected_by + + if (!collectedBy) { + errorObj[`collected_by`] = `payment.collected_by must be present in ${metroSequence.ON_SEARCH1}` + } else { + setValue(`collected_by`, collectedBy) + if (collectedBy !== 'BPP' && collectedBy !== 'BAP') { + errorObj['collected_by'] = + `payment.collected_by can only be either 'BPP' or 'BAP' in ${metroSequence.ON_SEARCH1}` + } + } + + // Validate payment tags + if (!payment?.tags) errorObj['payment.tags'] = `payment.tags is missing in ${metroSequence.ON_SEARCH1}` + + const tagsValidation = validatePaymentTags(payment?.tags, action) + if (!tagsValidation?.isValid) { + const dynamicKey = `${i}_tags` + Object.assign(errorObj, { [dynamicKey]: tagsValidation.errors }) + } + }) + } catch (error: any) { + logger.error(`!!Error occcurred while validating payments in /${constants.ON_SEARCH}, ${error.message}`) + } + } + + return errorObj +} + +export function checkItemsExist(items: any, newItemIDSValue: any, action: string) { + const errorObj: any = {} + try { + if (!items || items?.length === 0) { + errorObj['items'] = `/message/order/items is missing in ${action}` + } else { + logger.info(`Comparing item in /${action}`) + items?.forEach((item: any, index: number) => { + if (!item?.id) errorObj['itemId'] = `/message/order/items/id in ${index}: Item Id is Missing in ${action}` + else { + const getItemCount = getValue(`quantity_count`) + if (isNil(getItemCount)) setValue(`quantity_count`, getItemCount) + + if (item?.id && !newItemIDSValue.includes(item.id)) { + const key = `item[${index}].item_id` + errorObj[key] = + `/message/order/items/id in item: ${item.id} should be one of the /item/id mapped in previous call` + } + + if (!item?.quantity) errorObj[`item[${index}].quantity`] = `Item quantity is missing in ${action}` + + if (item?.quantity && !item?.quantity?.selected) + errorObj[`item[${index}].quantity_selected`] = `Item quantity selected is missing in ${action}` + + if (item?.quantity?.selected && !item?.quantity?.selected?.count) + errorObj[`item[${index}].quantity_selected_count`] = `Item quantity selected count is missing in ${action}` + } + }) + } + } catch (error: any) { + logger.error(`!!Error while comparing Item Id in /${constants.ON_SEARCH} and /${action}`) + } + + return errorObj +} + +interface BillingValidationResult { + [key: string]: string +} + +interface BillingInfo { + name?: string + email?: string + phone?: string +} + +export function checkBilling(billing: BillingInfo, action: string): BillingValidationResult { + const errorObj: BillingValidationResult = {} + + if (!billing) { + return errorObj + } + + try { + // Validate name + if (!billing.name?.trim()) { + errorObj['billing.name'] = `billing name must be present in /${action}` + } else { + setValue('billingName', billing.name) + } + + // Validate email + if (!billing.email?.trim()) { + errorObj['billing.email'] = `billing/email must be present in /${action}` + } else if (!isValidEmail(billing.email)) { + errorObj['billing.email'] = `billing/email must be valid Email in /${action}` + } + + // Validate phone + if (!billing.phone?.trim()) { + errorObj['billing.phone'] = `billing/phone must be present in /${action}` + } else if (!isValidPhoneNumber(billing.phone)) { + errorObj['billing.phone'] = `billing/phone must be valid Phone Number in /${action}` + } + } catch (error) { + logger.error( + `Error validating billing info for /${action}: ${error instanceof Error ? error.message : 'Unknown error'}`, + ) + } + + return errorObj +} + +function isValidEmail(email: string) { + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + return emailRegex.test(email) +} + +function isValidPhoneNumber(phoneNumber: string) { + const phoneRegex = /^(?:\+91-)?\d{10}$/ + return phoneRegex.test(phoneNumber) +} + +export function checkProviderTime(provider: any) { + const errorObj: any = {} + const { time } = provider || {} + const { range } = time || {} + + if (!time) { + errorObj.prvdrIdTime = `Provider Time is Missing in /${constants.ON_INIT}` + return errorObj + } + if (!range) { + errorObj.prvdrTimeRange = `Provider Time Range is Missing in /${constants.ON_INIT}` + return errorObj + } + + const { start, end } = range + + if (!start) { + errorObj.prvdrTimeRangeStart = `Provider Time Range Start is Missing in /${constants.ON_INIT}` + } else if (!isValidDateTime(start)) { + errorObj.prvdrStartFormat = `Range.start Time Format is Invalid in /${constants.ON_INIT}` + } + + if (!end) { + errorObj.prvdrTimeRangeEnd = `Provider Time Range End is Missing in /${constants.ON_INIT}` + } else if (!isValidDateTime(end)) { + errorObj.prvdrEndFormat = `Range.end Time Format is Invalid in /${constants.ON_INIT}` + } + + return errorObj +} + +function isValidDateTime(dateTimeString: string): boolean { + const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/ + return regex.test(dateTimeString) +} + +export function validateFarePolicyTags(tags: { [key: string]: any }[], i: number, j: number, action: string) { + const errorObj: string[] = [] + try { + tags?.forEach((tag) => { + if (tag?.descriptor?.code !== 'FARE_POLICY') + errorObj.push(`Tag descriptor code should be 'FARE_POLICY' in /providers[${i}]/items[${j}]`) + else { + let hasRestrictedPerson = false + let hasRestrictedProof = false + + tag?.list?.forEach((item: any, itemIndex: number) => { + const descriptorCode = item.descriptor.code + + // Check if descriptor code is not in uppercase + if (descriptorCode !== descriptorCode.toUpperCase()) { + errorObj.push(`Code should be in uppercase at Fare Policy Tag[${i}], List item[${itemIndex}].`) + } + + // Validate known descriptor codes + if (descriptorCode?.toUpperCase() === 'RESTRICTED_PERSON') { + hasRestrictedPerson = true // Mark ROUTE_ID as found + if (typeof item.value !== 'string') { + errorObj.push( + `Fare Policy Tag[${i}], List item[${itemIndex}] has an invalid value for RESTRICTED_PERSON. It should be a string.`, + ) + } + } + if (descriptorCode?.toUpperCase() === 'RESTRICTED_PROOF') { + hasRestrictedProof = true // Mark ROUTE_DIRECTION as found + if (typeof item.value !== 'string') { + errorObj.push( + `Fare Policy Tag[${i}], List item[${itemIndex}] has an invalid value for RESTRICTED_PROOF. It should be a string.`, + ) + } + } + }) + + // Check if both RESTRICTED_PERSON and RESTRICTED_PROOF are present + if (!hasRestrictedPerson) { + errorObj.push(`Fare Policy tag[${i}] is missing RESTRICTED_PERSON.`) + } + + if (!hasRestrictedProof) { + errorObj.push(`Fare Policy tag[${i}] is missing RESTRICTED_PROOF.`) + } + } + }) + } catch (error: any) { + logger.error(`!!Error in Provider Time of /${action}`) + } + + return errorObj +} + +export function validateParams( + params: { [key: string]: any }, + collected_by: string, + action: string, + payment_type: string = 'UPI', +) { + const errorObj: any = {} + const requiredParams = ['bank_code', 'bank_account_number'] + const collectedBy = collected_by.toUpperCase() + + try { + // Early return if params validation is not required + if ( + (action === constants.INIT && collectedBy === 'BAP') || + (action === constants.ON_INIT && collectedBy === 'BPP') + ) { + if (params && !isEmpty(params)) + errorObj.payment_params = `params must not be present in /${action} if collector is ${collected_by}.` + else if (params && isEmpty(params)) + errorObj.payment_params = `params object must not be present and non-empty in /${action} if collector is ${collected_by}` + + return errorObj + } + + // Check if params exist when required + if (!params || isEmpty(params)) { + errorObj.payment_params = `params object must be present and non-empty in /${action}` + + return errorObj + } + + // Validate required parameters + let requiredParmsToCheck = action?.includes(constants.INIT) + ? requiredParams + : [...requiredParams, 'amount', 'currency', 'transaction_id'] + + requiredParmsToCheck = + payment_type === 'UPI' ? [...requiredParmsToCheck, 'virtual_payment_address'] : requiredParmsToCheck + + for (const param of requiredParmsToCheck) { + if (!(param in params)) { + errorObj[`payment_params_${param}`] = `params.${param} must be present in /${action}` + + continue + } + + const storedValue = getValue(`payment_${param}`) + if (isNil(storedValue)) setValue(`payment_${param}`, params[param]) + else if (storedValue !== params[param]) + errorObj[`payment_params_${param}`] = `params.${param} must be same as sent in previous call.` + } + + return errorObj + } catch (error) { + logger.info(`Error validating params in /${action}`) + return errorObj + } +} + +export function validateFulfillmentV2_0( + fulfillment: any, + errorObj: any, + action: string, + flow: { flow: string; flowSet: string }, +) { + try { + console.log(flow) + const getItemCount = getValue(`quantity_count`) ?? 0 + if (fulfillment?.length !== Number(getItemCount) + 1) + errorObj.fulfillment = `fulfillment length must be count ${Number(getItemCount) + 1} in /${action}` + else { + fulfillment.forEach((item: any, index: number) => { + if (!item?.id) errorObj.fulfillment = `fulfillment[${index}] must have id in /${action}` + }) + } + } catch (error) { + logger.info(`Error validating fulfillment in /${action}`) + return errorObj + } +} + +export function validateDomain(domainName: string) { + return domainName === 'METRO' ? true : false +} + export function validateFulfillmentStops(stops: any) { - const errorObj: Record = {} - - const stopTypes = new Set() - - for (const stop of stops) { - if (!stop?.type) { - if (!errorObj['stops']) errorObj['stops'] = [] - errorObj['stops'].push("Each stop must have a 'type' (START or END)") - } else { - stopTypes.add(stop.type) + const errorObj: Record = {} + + const stopTypes = new Set() + + for (const stop of stops) { + if (!stop?.type) { + if (!errorObj['stops']) errorObj['stops'] = [] + errorObj['stops'].push("Each stop must have a 'type' (START or END)") + } else { + stopTypes.add(stop.type) + } + } + + if (!stopTypes.has('START') && !stopTypes.has('END')) { + return { stops: ["Stops must include both 'START' and 'END' types"] } + } + + if (!stopTypes.has('START')) { + errorObj['stops'] = ["Stops must include a 'START' type"] + } else if (!stopTypes.has('END')) { + errorObj['stops'] = ["Stops must include an 'END' type"] + } + + return Object.keys(errorObj).length ? errorObj : null +} + +export function validateQuotePricing(quote: any) { + const errorObj: Record = {}; + let calculatedTotal = 0; + + for (const item of quote.breakup) { + const itemPrice = parseFloat(item?.price?.value) || 0; + calculatedTotal += itemPrice; + + if (item.item?.quantity?.selected?.count) { + const quantity = item.item.quantity.selected.count || 0; + const individualPrice = parseFloat(item.item?.price?.value) || 0; + const expectedPrice = quantity * individualPrice; + if (expectedPrice !== itemPrice) { + errorObj[`quote:${item.item.id}`|| "unknown"] = `Mismatch in price calculation: Expected ${expectedPrice}, Found ${itemPrice}`; } } + } + console.log('Calculated Total Price: ', calculatedTotal); + console.log('Given Total Price: ', quote?.price?.value) + + const totalPrice = parseFloat(quote?.price?.value) || 0; + if (calculatedTotal !== totalPrice) { + errorObj["quote:total"] = `Total price mismatch: Expected ${totalPrice}, Calculated ${calculatedTotal}`; + } + + if (Object.keys(errorObj).length) { + return { success: false, data: errorObj }; + } + return { success: true, data: "Validation successful" }; +} + +export function compareItems(items: any[], storedItemIDS: string[]): { success: boolean; data: Record } { + const parentItems: Record = {}; + const childItems: Record = {}; + const errorObj: Record = {}; - if (!stopTypes.has('START') && !stopTypes.has('END')) { - return { stops: ["Stops must include both 'START' and 'END' types"] } + items.forEach((item: any, index: number) => { + const itemId = item.id; + const parentItemId = item.parent_item_id; + const itemCount = item?.quantity?.selected?.count || 0; + + if (itemId) { + parentItems[itemId] = itemCount; + } else if (parentItemId) { + if (itemCount !== 1) { + errorObj[`item[${parentItemId}].quantity_count`] = + `Parent item ${parentItemId} should have total selected count of 1`; + } + childItems[parentItemId] = (childItems[parentItemId] || 0) + 1; } - - if (!stopTypes.has('START')) { - errorObj['stops'] = ["Stops must include a 'START' type"] - } else if (!stopTypes.has('END')) { - errorObj['stops'] = ["Stops must include an 'END' type"] + + if (storedItemIDS && !storedItemIDS.includes(itemId)) { + errorObj[`item[${index}].item_id`] = + `/message/order/items/id in item: ${itemId} should be one of the /item/id mapped in previous call`; } + }); + + Object.keys(childItems).forEach((parentId) => { + if (parentItems[parentId] !== childItems[parentId]) { + errorObj[`item[${parentId}].quantity_count`] = + `Parent item ${parentId} has total count ${parentItems[parentId]}, but child items sum up to ${childItems[parentId]}`; + } + }); + + return { + success: Object.keys(errorObj).length === 0, + data: errorObj, + }; +} + +export function processFulfillments(fulfillments : any) { + let tripDetails = null; + let ticketCount = 0; + let totalSeatPrice = 0; + let seatNumbers : any = []; - return Object.keys(errorObj).length ? errorObj : null - } - \ No newline at end of file + fulfillments.forEach((fulfillment : any) => { + if (fulfillment.type === "TRIP") { + tripDetails = { + id: fulfillment.id, + vehicleCategory: fulfillment.vehicle?.category || "", + vehicleCode: fulfillment.vehicle?.code || "", + operatedBy: fulfillment.tags?.find((tag : any) => tag.descriptor?.code === "INFO")?.list?.find((item : any) => item.descriptor?.code === "OPERATED_BY")?.value || "", + stops: fulfillment.stops?.map((stop : any) => ({ + id: stop.id, + type: stop.type, + location: stop.location?.descriptor?.name || "", + code: stop.location?.descriptor?.code || "", + timestamp: stop.time?.timestamp || "" + })) || [] + }; + } + + if (fulfillment.type === "TICKET") { + ticketCount++; + let seatPrice = 0; + let seatNumber = ""; + + fulfillment.tags?.forEach((tag : any) => { + if (tag.descriptor?.code === "SEAT_GRID") { + tag.list?.forEach((item : any) => { + if (item.descriptor?.code === "SEAT_PRICE") { + seatPrice = parseFloat(item.value) || 0; + } + if (item.descriptor?.code === "SEAT_NUMBER") { + seatNumber = item.value; + } + }); + } + }); + totalSeatPrice += seatPrice; + if (seatNumber) { + seatNumbers.push(seatNumber); + } + } + }); + + return { + tripDetails, + ticketCount, + totalSeatPrice, + seatNumbers + }; +} + diff --git a/utils/mobility/TRV12/init.ts b/utils/mobility/TRV12/init.ts index 27250c82..eef33389 100644 --- a/utils/mobility/TRV12/init.ts +++ b/utils/mobility/TRV12/init.ts @@ -6,6 +6,7 @@ import { validateContext } from '../../metro/metroChecks' import { isEmpty, isNil } from 'lodash' import { checkBilling, checkItemsExist, validateParams } from '../../metro/validate/helper' import { validatePaymentTags } from '../tags' +import { compareItems } from './functions/helper' export const checkInit = (data: any, msgIdSet: any) => { const errorObj: any = {} @@ -20,7 +21,7 @@ export const checkInit = (data: any, msgIdSet: any) => { } const schemaValidation = validateSchema('TRV12', constants.INIT, data) - const contextRes: any = validateContext(context, msgIdSet, constants.ON_SEARCH, constants.INIT) + const contextRes: any = validateContext(context, msgIdSet, constants.ON_SELECT, constants.INIT) setValue(`${airlinesSequence.INIT}_message`, message) if (schemaValidation !== 'error') { @@ -72,6 +73,10 @@ export const checkInit = (data: any, msgIdSet: any) => { //check items const getItemError = checkItemsExist(init?.items, newItemIDSValue, constants.INIT) if (Object.keys(getItemError)?.length) Object.assign(errorObj, getItemError) + const checkItems = compareItems(init?.items, newItemIDSValue) + if(!checkItems.success){ + Object.assign(errorObj, checkItems.data) + } try { logger.info(`Checking payments in /${constants.INIT}`) diff --git a/utils/mobility/TRV12/onCancel.ts b/utils/mobility/TRV12/onCancel.ts index c9c0518f..21fc903a 100644 --- a/utils/mobility/TRV12/onCancel.ts +++ b/utils/mobility/TRV12/onCancel.ts @@ -7,9 +7,9 @@ import { validateSchema, isObjectEmpty } from '../../' import { validateContext } from '../../metro/metroChecks' import { validateParams } from '../../metro/validate/helper' import { isEmpty } from 'lodash' +import { compareItems, validateQuotePricing } from './functions/helper' const VALID_VEHICLE_CATEGORIES = ['AUTO_RICKSHAW', 'CAB', 'METRO', 'BUS', 'AIRLINE'] -const VALID_DESCRIPTOR_CODES = ['RIDE', 'SJT', 'SFSJT', 'PASS', 'SEAT', 'NON STOP', 'CONNECT', 'RJT'] const validTypes = ['PRE-ORDER', 'ON-FULFILLMENT', 'POST-FULFILLMENT'] const validStatus = ['NOT-PAID', 'PAID'] @@ -25,6 +25,7 @@ export const checkOnCancelPayload = ( if (!data || isObjectEmpty(data)) { return { [constants.ON_CANCEL]: 'Json cannot be empty' } } + const onConfirmMessage = getValue('on_confirm_message') const { message, context }: any = data if (!message || !context || !message.order || isObjectEmpty(message) || isObjectEmpty(message.order)) { @@ -129,7 +130,7 @@ export const checkOnCancelPayload = ( if (fulfillment.tags) { // Validate route info tags - if (String(flow.flow.toUpperCase()).includes('INTRACITY')) { + if (String(flow.flow.toUpperCase()).includes('AIRLINES')) { const tagsValidation = validateRouteInfoTags(fulfillment.tags) if (!tagsValidation.isValid) { Object.assign(errorObj, { tags: tagsValidation.errors }) @@ -143,39 +144,17 @@ export const checkOnCancelPayload = ( } try { - on_cancel?.items && - on_cancel?.items.forEach((item: any, index: number) => { - if (!isEmpty(newItemIDSValue) && !newItemIDSValue.includes(item.id)) { - const key = `item[${index}].item_id` - errorObj[key] = - `/message/order/items/id in item: ${item.id} should be one of the /item/id mapped in /${constants.ON_CANCEL}` - } + if(on_cancel?.items){ + const itemsCheck = compareItems(on_cancel?.items, newItemIDSValue); + if(!itemsCheck.success){ + Object.assign(errorObj, itemsCheck.data) - if (!item.descriptor || !item.descriptor.code) { - const key = `item${index}_descriptor` - errorObj[key] = `Descriptor is missing in items[${index}]` - } else { - if (!VALID_DESCRIPTOR_CODES.includes(item.descriptor.code)) { - const key = `item${index}_descriptor` - errorObj[key] = - `descriptor.code should be one of ${VALID_DESCRIPTOR_CODES} instead of ${item.descriptor.code}` - } - } - - const price = item.price - if (!price || !price.currency || !price.value) { - const key = `item${index}_price` - errorObj[key] = `Price is incomplete in /items[${index}]` - } + } + if(onConfirmMessage?.order?.items && JSON.stringify(onConfirmMessage?.order?.items) !== JSON.stringify(on_cancel?.items)){ + errorObj[`Items:error`] = `Items mismatch in ${constants.ON_CONFIRM} and ${constants.ON_CANCEL}` + } - item.fulfillment_ids && - item.fulfillment_ids.forEach((fulfillmentId: string) => { - if (!fulfillmentIdsSet.has(fulfillmentId)) { - errorObj[`invalidItemFulfillmentId_${index}`] = - `Item Fulfillment ID should be one of the fulfillment id '${fulfillmentId}' at index ${index} in /${constants.ON_CANCEL}.` - } - }) - }) + } } catch (error: any) { logger.error(`!!Error occcurred while checking items info in /${constants.ON_CANCEL}, ${error.message}`) return { error: error.message } @@ -256,8 +235,15 @@ export const checkOnCancelPayload = ( try { logger.info(`Checking quote details in /${constants.ON_CANCEL}`) + const quotePriceCheck = validateQuotePricing(on_cancel?.quote) + if(!quotePriceCheck.success){ + Object.assign(errorObj, quotePriceCheck.data) + } const quoteErrors = validateQuote(on_cancel?.quote, constants.ON_CANCEL) Object.assign(errorObj, quoteErrors) + if(onConfirmMessage?.order?.quote && JSON.stringify(onConfirmMessage?.order?.quote) !== JSON.stringify(on_cancel?.quote)){ + errorObj[`Quote:error`] = `Quote mismatch in ${constants.ON_CONFIRM} and ${constants.ON_CANCEL}` + } } catch (error: any) { logger.error(`!!Error occcurred while checking Quote in /${constants.ON_CANCEL}, ${error.message}`) return { error: error.message } diff --git a/utils/mobility/TRV12/onConfirm.ts b/utils/mobility/TRV12/onConfirm.ts index 5688eba4..9e87707a 100644 --- a/utils/mobility/TRV12/onConfirm.ts +++ b/utils/mobility/TRV12/onConfirm.ts @@ -7,13 +7,14 @@ import { validateSchema, isObjectEmpty } from '../../' import { validateContext } from '../../metro/metroChecks' import { isEmpty, isNil } from 'lodash' import { validateFulfillmentV2_0, validateParams } from '../../metro/validate/helper' +import { compareItems, validateQuotePricing } from './functions/helper' -// const VALID_VEHICLE_CATEGORIES = ['AUTO_RICKSHAW', 'CAB', 'METRO', 'BUS', 'AIRLINE'] -const VALID_DESCRIPTOR_CODES = ['RIDE', 'SJT', 'SFSJT', 'PASS', 'SEAT', 'NON STOP', 'CONNECT', 'RJT'] +const VALID_DESCRIPTOR_CODES = ['ADULT_TICKET', 'CHILD_TICKET'] export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; flowSet: string }, version: string) => { const errorObj: any = {} const payment = getValue('paymentId') + const onInitMessage = getValue('on_init_message'); // const paymentAmount = getValue('paramsAmount') // const paymentTransactionId = getValue('paramsTransactionId') try { @@ -43,7 +44,6 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f const itemIdArray: any[] = [] const fulfillmentIdsSet = new Set() const storedFull: any = getValue(`${airlinesSequence.ON_INIT}_storedFulfillments`) - // const getFulfillmentId = getValue('StoredFulfillmentId') || [] let newItemIDSValue: any[] @@ -98,10 +98,11 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f if (!fulfillment?.vehicle) { errorObj['Vehicle'] = 'Vehicle Object Is Missing' } else if ( - fulfillment?.vehicle?.category !== (String(flow?.flow).toUpperCase() !== 'METRO' ? 'BUS' : 'METRO') + fulfillment?.vehicle?.category !== + (String(flow?.flow).toUpperCase() !== 'AIRLINES' ? 'AIRLINE' : 'AIRLINE') ) { errorObj[`${fulfillmentKey}.vehicleCategory`] = - `Vehicle category should be ${String(flow?.flow).toUpperCase() !== 'METRO' ? 'BUS' : 'METRO'} in Fulfillment.` + `Vehicle category should be ${String(flow?.flow).toUpperCase() !== 'AIRLINES' ? 'AIRLINES' : 'AIRLINES'} in Fulfillment.` } if (fulfillment.type !== 'TRIP') { @@ -115,9 +116,9 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f if (Object.keys(getStopsError).length > 0 && Object.keys(errorValue)?.length) Object.assign(errorObj, getStopsError) - if (String(flow?.flow).toUpperCase() !== 'METRO') { + if (String(flow?.flow).toUpperCase() !== 'AIRLINES') { if (!fulfillment?.tags) - errorObj[`ulfillment_${index}_tags`] = `Tags should be present in Fulfillment in case of Intracity.` + errorObj[`ulfillment_${index}_tags`] = `Tags should be present in Fulfillment in case of Airline.` else { // Validate route info tags const tagsValidation = validateRouteInfoTags(fulfillment?.tags) @@ -170,6 +171,15 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f } }) }) + + const itemsCheck = compareItems(on_confirm?.items, itemIDS); + if(!itemsCheck.success){ + Object.assign(errorObj, itemsCheck.data) + } + + if(onInitMessage?.items && JSON.stringify(onInitMessage?.items) !== JSON.stringify(on_confirm?.items)){ + errorObj[`Items:error`] = `Items mismatch in ${constants.ON_INIT} and ${constants.ON_CONFIRM}` + } } catch (error: any) { logger.error(`!!Error occcurred while checking items info in /${constants.ON_CONFIRM}, ${error.message}`) return { error: error.message } @@ -215,50 +225,7 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f const payment_type = getValue('INIT_PAYMENT_TYPE') ?? 'NEFT' const validatePayementParams = validateParams(arr.params, arr?.collected_by, constants.ON_CONFIRM, payment_type) if (!isEmpty(validatePayementParams)) Object.assign(errorObj, validatePayementParams) - // if (!params) { - // errorObj[`payments[${i}]_params`] = `payments.params must be present in ${constants.CONFIRM}` - // } else { - // const { amount, currency, transaction_id } = params - - // if (!amount) { - // errorObj[`payments[${i}]_params_amount`] = - // `payments.params.amount must be present in ${constants.ON_CONFIRM}` - // } else if (amount !== paymentAmount) { - // errorObj[`payments[${i}]_params_amount`] = - // `payments.params.amount must match with the ${paymentAmount} in ${constants.ON_CONFIRM}` - // } - - // if (!currency) { - // errorObj[`payments[${i}]_params_currency`] = - // `payments.params.currency must be present in ${constants.CONFIRM}` - // } else if (currency !== 'INR') { - // errorObj[`payments[${i}]_params_currency`] = - // `payments.params.currency must be INR in ${constants.ON_CONFIRM}` - // } - - // if (!transaction_id) { - // errorObj[`payments[${i}]_params_transaction_id`] = - // `payments.params.transaction_id must be present in ${constants.ON_CONFIRM}` - // } else if (transaction_id !== paymentTransactionId) { - // errorObj[`payments[${i}]_params_transaction_id`] = - // `payments.params.transaction_id must match with the ${paymentTransactionId} in ${constants.ON_CONFIRM}` - // } - // } - // // Validate bank_code - // validatePaymentParams(params, bankCode, 'bank_code', errorObj, i, constants.ON_CONFIRM) - - // // Validate bank_account_number - // validatePaymentParams(params, bankAccountNumber, 'bank_account_number', errorObj, i, constants.ON_CONFIRM) - - // // Validate virtual_payment_address - // validatePaymentParams( - // params, - // virtualPaymentAddress, - // 'virtual_payment_address', - // errorObj, - // i, - // constants.ON_CONFIRM, - // ) + if (arr.time) { if (!arr.label || arr.label !== 'INSTALLMENT') { @@ -278,8 +245,15 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f try { logger.info(`Checking quote details in /${constants.ON_CONFIRM}`) + const quotePriceCheck = validateQuotePricing(on_confirm?.quote) + if(!quotePriceCheck.success){ + Object.assign(errorObj, quotePriceCheck.data) + } const quoteErrors = validateQuote(on_confirm?.quote, constants.ON_CONFIRM) Object.assign(errorObj, quoteErrors) + if(onInitMessage?.order?.quote && JSON.stringify(onInitMessage?.order?.quote) !== JSON.stringify(on_confirm?.quote)){ + errorObj[`Quote:error`] = `Quote mismatch in ${constants.ON_INIT} and ${constants.ON_CONFIRM}` + } } catch (error: any) { logger.error(`!!Error occcurred while checking Quote in /${constants.ON_CONFIRM}, ${error.message}`) return { error: error.message } @@ -287,7 +261,7 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f try { logger.info(`Checking cancellation terms in /${constants.ON_CONFIRM}`) - if (String(flow?.flow)?.toUpperCase() === 'METRO') { + if (String(flow?.flow)?.toUpperCase() === 'AIRLINES') { if (!on_confirm?.cancellation_terms) errorObj.cancellation_terms = `cancellation_terms missing in /${constants.ON_CONFIRM}` else { @@ -311,7 +285,7 @@ export const checkOnConfirm = (data: any, msgIdSet: any, flow: { flow: string; f logger.error(`!!Error while checking status in message.order object /${constants.ON_CONFIRM}, ${error.stack}`) } - if (String(flow?.flow)?.toUpperCase() === 'METRO' && on_confirm?.tags) { + if (String(flow?.flow)?.toUpperCase() === 'AIRLINES' && on_confirm?.tags) { const tagsValidation: { [key: string]: any } | null = validateTags(on_confirm?.tags ?? [], 0) if (!isNil(tagsValidation)) Object.assign(errorObj, tagsValidation) } diff --git a/utils/mobility/TRV12/onInit.ts b/utils/mobility/TRV12/onInit.ts index 91c1263c..213fb11f 100644 --- a/utils/mobility/TRV12/onInit.ts +++ b/utils/mobility/TRV12/onInit.ts @@ -1,11 +1,10 @@ import { logger } from '../../../shared/logger' import { getValue, setValue } from '../../../shared/dao' import { validatePaymentTags, validateRouteInfoTags, validateTags } from '../../metro/tags' -import constants, { airlinesSequence } from '../../../constants' +import constants, { airlinesSequence, metroSequence } from '../../../constants' import { validateCancellationTerms, validateDescriptor, - validateItemDescriptor, validateQuote, validateStops, } from '../../metro/metroChecks' @@ -13,13 +12,12 @@ import { validateSchema, isObjectEmpty } from '../../' import { validateContext } from '../../metro/metroChecks' import { isEmpty, isNil } from 'lodash' import { - checkItemTime, checkProviderTime, validateFulfillmentV2_0, validateParams, } from '../../metro/validate/helper' +import { compareItems, validateQuotePricing } from './functions/helper' -const VALID_DESCRIPTOR_CODES = ['SJT', 'SFSJT', 'RJT', 'PASS'] export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flowSet: string }, version: string) => { try { const errorObj: any = {} @@ -27,7 +25,7 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow if (!data || isObjectEmpty(data)) { return { [airlinesSequence.ON_INIT]: 'Json cannot be empty' } } - + const onSelectMessage = getValue('on_select_message') const { message, context }: any = data if (!message || !context || !message.order || isObjectEmpty(message) || isObjectEmpty(message.order)) { return { missingFields: '/context, /message, /order or /message/order is missing or empty' } @@ -66,7 +64,7 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow //check provider try { - if (String(flow?.flow)?.toUpperCase() === 'METRO' && on_init?.tags) { + if (String(flow?.flow)?.toUpperCase() === 'AIRLINES' && on_init?.tags) { const tagsValidation: { [key: string]: any } | null = validateTags(on_init?.tags ?? [], 0) if (!isNil(tagsValidation)) Object.assign(errorObj, tagsValidation) } @@ -99,7 +97,7 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow //check time validation // fix checkProviderTime also - if (String(flow?.flow).toUpperCase() === 'METRO') { + if (String(flow?.flow).toUpperCase() === 'AIRLINES') { const checkTimeError = checkProviderTime(on_init?.provider) if (Object.keys(checkTimeError).length > 0) Object.assign(errorObj, checkTimeError) } @@ -132,9 +130,9 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow errorObj[`Fulfillment[${index}].id`] = `fulfillment.id missing in /${constants.ON_INIT}` } - if (fulfillment?.vehicle?.category !== (String(flow?.flow).toUpperCase() !== 'METRO' ? 'BUS' : 'METRO')) { + if (fulfillment?.vehicle?.category !== (String(flow?.flow).toUpperCase() !== 'AIRLINES' ? 'AIRLINES' : 'AIRLINES')) { errorObj[`${fulfillmentKey}.vehicleCategory`] = - `Vehicle category should be ${String(flow?.flow).toUpperCase() !== 'METRO' ? 'BUS' : 'METRO'} in Fulfillment.` + `Vehicle category should be ${String(flow?.flow).toUpperCase() !== 'AIRLINES' ? 'AIRLINES' : 'AIRLINES'} in Fulfillment.` } if (fulfillment.type !== 'TRIP') { @@ -149,7 +147,7 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow if (Object.keys(getStopsError).length > 0 && Object.keys(errorValue)?.length) Object.assign(errorObj, getStopsError) - if (String(flow?.flow).toUpperCase() !== 'METRO') { + if (String(flow?.flow).toUpperCase() !== 'AIRLINES') { if (!fulfillment?.tags) errorObj[`ulfillment_${index}_tags`] = `Tags should be present in Fulfillment in case of Intracity.` else { @@ -175,67 +173,14 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow try { //items null check if (!on_init?.items) errorObj.items = `Items missing in /${constants.ON_INIT}` - else { - on_init?.items?.forEach((item: any, index: number) => { - //handle id non-existant check - if (!item.id) errorObj[`item[${index}].id`] = `Item id missing in /${constants.ON_INIT}` - else if (!newItemIDSValue.includes(item.id)) { - const key = `item[${index}].item_id` - errorObj[key] = - `/message/order/items/id in item: ${item.id} should be one of the /item/id mapped in /${constants.ON_INIT}` - } - - //use common descriptor function - const getDescriptorError = validateItemDescriptor(item, index, VALID_DESCRIPTOR_CODES) - if (Object.keys(getDescriptorError)?.length) Object.assign(errorObj, getDescriptorError) - - //handle price non-existant check - const price = item?.price - if (!price || !price.currency || !price.value) { - const key = `item${index}_price` - errorObj[key] = `Price is incomplete or missing in /items[${index}]` - } - - //missing time check - const itemTimeError = checkItemTime(item?.time, 0, index) - if (!isNil(itemTimeError)) Object.assign(errorObj, itemTimeError) - - //missing category_ids check - if (String(flow?.flow)?.toUpperCase() === 'METRO') { - if (!item?.category_ids) - errorObj[`item${index}category_ids`] = `Category_ids array is missing in /${constants.ON_INIT}` - else { - if (item?.category_ids && !item?.category_ids.length) - errorObj[`item${index}category_ids`] = `Category_ids array is empty in /${constants.ON_INIT}` - } - } - - //missing quantity check - - if (!item?.quantity) { - errorObj[`item${index}quantity`] = `Quantity is missing in /${constants.ON_INIT}` - } else { - if (!item?.quantity?.selected) - errorObj[`item${index}quantity_selected`] = `Quantity selected is missing in /${constants.ON_INIT}` - - if (!item?.quantity?.selected?.count) - errorObj[`item${index}quantity_selected_count`] = - `Quantity selected count is missing in /${constants.ON_INIT}` - } - - //handle fulfillment_ids non-existant check - if (!item?.fulfillment_ids) - errorObj[`item${index}fulfillment_ids`] = `Fulfillment ids missing in /${constants.ON_INIT}` - else { - item?.fulfillment_ids.forEach((fulfillmentId: string) => { - if (!fulfillmentIdsSet.has(fulfillmentId)) { - errorObj[`invalidItemFulfillmentId_${index}`] = - `Fulfillment ID should be one of the fulfillment id '${fulfillmentId}' at index ${index} in /${constants.ON_INIT} is not valid` - } - }) - } - }) + const itemCheck = compareItems(on_init?.items, newItemIDSValue) + if(!itemCheck.success) { + Object.assign(errorObj, itemCheck.data) } + if(onSelectMessage?.order?.items && JSON.stringify(on_init?.items) != JSON.stringify(onSelectMessage?.order?.items)){ + errorObj[`Items:error`] = `Items mismatch in ${metroSequence.ON_SELECT} and ${metroSequence.ON_INIT}` + } + } catch (error: any) { logger.error(`!!Error occcurred while checking items info in /${constants.ON_INIT}, ${error.message}`) return { error: error.message } @@ -244,7 +189,7 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow //check cancellation_terms // revisit & handle external_ref check try { - if (String(flow?.flow)?.toUpperCase() === 'METRO') { + if (String(flow?.flow)?.toUpperCase() === 'AIRLINES') { if (!on_init?.cancellation_terms) errorObj.cancellation_terms = `cancellation_terms missing in /${constants.ON_INIT}` else { @@ -316,7 +261,16 @@ export const checkOnInit = (data: any, msgIdSet: any, flow: { flow: string; flow //check quote try { logger.info(`Checking quote details in /${constants.ON_INIT}`) + const quotePriceCheck = validateQuotePricing(on_init?.quote) + if(!quotePriceCheck.success){ + Object.assign(errorObj, quotePriceCheck.data) + } const quoteErrors = validateQuote(on_init?.quote, constants.ON_INIT) + + + if(onSelectMessage?.order?.quote && JSON.stringify(on_init?.quote) !== JSON.stringify(onSelectMessage?.order?.quote)){ + errorObj[`Quote:error`] = `Quote mismatch in ${metroSequence.ON_SELECT} and ${metroSequence.ON_INIT}` + } Object.assign(errorObj, quoteErrors) } catch (error: any) { logger.error(`!!Error occcurred while checking Quote in /${constants.ON_INIT}, ${error.message}`) diff --git a/utils/mobility/TRV12/onSearch.ts b/utils/mobility/TRV12/onSearch.ts index b341f854..0058dc1a 100644 --- a/utils/mobility/TRV12/onSearch.ts +++ b/utils/mobility/TRV12/onSearch.ts @@ -1,6 +1,6 @@ import { logger } from '../../../shared/logger' import { setValue } from '../../../shared/dao' -import { validateRouteInfoTags, validateTags } from '../../metro/tags' +import { validateRouteInfoTags } from '../../metro/tags' import constants, { airlinesSequence } from '../../../constants' import { validateDescriptor, validateStops } from '../../metro/metroChecks' import { validateSchema, isObjectEmpty } from '../../' @@ -13,9 +13,9 @@ import { checkPayment, checkRefIds, validateFarePolicyTags, -} from '../../metro/validate/helper' -import { VALID_DESCRIPTOR_CODES } from '../../metro/enum' +} from './functions/helper' +export const VALID_DESCRIPTOR_CODES = ['ADULT_TICKET', 'CHILD_TICKET'] export const checkOnSearch = ( data: any, msgIdSet: any, @@ -35,8 +35,8 @@ export const checkOnSearch = ( } const contextRes: any = validateContext(context, msgIdSet, constants.SEARCH, constants.ON_SEARCH) - const schemaValidation = validateSchema('TRV12', constants.ON_SEARCH, data) - setValue(`${airlinesSequence.ON_SEARCH}_message`, message) + const schemaValidation = validateSchema(flow?.flow === 'Airlines' ? 'TRV12' : 'TRV12BUS', constants.ON_SEARCH, data) + setValue(`${constants.ON_SEARCH}_message`, message) if (schemaValidation !== 'error') { Object.assign(errorObj, schemaValidation) @@ -71,16 +71,10 @@ export const checkOnSearch = ( while (i < len) { //validate categories const categories = onSearchCatalog['providers'][i]?.categories - if (String(flow?.flow)?.toUpperCase() === 'METRO' && onSearchCatalog['providers'][i]?.tags) { - const tagsValidation: { [key: string]: any } | null = validateTags( - onSearchCatalog['providers'][i]?.tags ?? [], - i, - ) - if (!isNil(tagsValidation)) Object.assign(errorObj, tagsValidation) - } + // } - if (String(flow.flow)?.toUpperCase() === 'METRO') { + if (String(flow.flow)?.toUpperCase() === 'AIRLINES') { const providerTime = onSearchCatalog['providers'][i]?.time if (providerTime) { @@ -98,7 +92,7 @@ export const checkOnSearch = ( } const categoriesIds: string[] = [] - if (String(flow?.flow)?.toUpperCase() === 'METRO') { + if (String(flow?.flow)?.toUpperCase() === 'AIRLINES') { if (categories) { onSearchCatalog['providers'][i]?.categories?.map( (item: { id: string; descriptor: { code: string } }, index: number) => { @@ -143,7 +137,7 @@ export const checkOnSearch = ( storedFulfillments.add(fulfillment.id) } - if (String(flow?.flow).toUpperCase() !== 'METRO') { + if (String(flow?.flow).toUpperCase() !== 'AIRLINES') { if (!fulfillment?.tags) errorObj[`provider_${i}_fulfillment_${k}tags`] = `Tags should be present in Fulfillment in case of Intracity.` diff --git a/utils/mobility/TRV12/onSelect.ts b/utils/mobility/TRV12/onSelect.ts index 7a48433c..23352fa9 100644 --- a/utils/mobility/TRV12/onSelect.ts +++ b/utils/mobility/TRV12/onSelect.ts @@ -6,8 +6,8 @@ import { validateQuote, validateStops } from '../../metro/metroChecks' import { validateSchema, isObjectEmpty } from '../../' import { validateContext } from '../../metro/metroChecks' import { validateFulfillmentV2_0 } from '../../metro/validate/helper' +import { compareItems, validateQuotePricing } from './functions/helper' -const VALID_DESCRIPTOR_CODES = ['RIDE', 'SJT', 'SFSJT', 'PASS', 'SEAT', 'NON STOP', 'CONNECT', 'RJT'] export const checkOnSelect = (data: any, msgIdSet: any, flow: { flow: string; flowSet: string }, version: string) => { if (!data || isObjectEmpty(data)) { return { [metroSequence.ON_SELECT]: 'Json cannot be empty' } @@ -19,7 +19,7 @@ export const checkOnSelect = (data: any, msgIdSet: any, flow: { flow: string; fl return { missingFields: '/context, /message, /order or /message/order is missing or empty' } } - const schemaValidation = validateSchema('TRV12', constants.ON_SELECT, data) + const schemaValidation = validateSchema(flow?.flow === 'Airlines' ? 'TRV12' : 'TRV12BUS', constants.ON_SELECT, data) const contextRes: any = validateContext(context, msgIdSet, constants.SELECT, constants.ON_SELECT) setValue(`${metroSequence.ON_SELECT}_message`, message) @@ -40,7 +40,6 @@ export const checkOnSelect = (data: any, msgIdSet: any, flow: { flow: string; fl const itemIdArray: any[] = [] const storedFull: any = getValue(`${metroSequence.ON_SEARCH1}_storedFulfillments`) // const fulfillmentIdsSet = new Set() - const itemIdsSet = new Set() try { logger.info(`Comparing Provider Id of /${constants.SELECT} and /${constants.ON_SELECT}`) @@ -74,9 +73,9 @@ export const checkOnSelect = (data: any, msgIdSet: any, flow: { flow: string; fl // fulfillmentIdsSet.add(fulfillment.id) // } - if (fulfillment?.vehicle?.category !== (String(flow?.flow).toUpperCase() !== 'METRO' ? 'BUS' : 'METRO')) { + if (fulfillment?.vehicle?.category !== (String(flow?.flow).toUpperCase() !== 'AIRLINE' ? 'BUS' : 'METRO')) { errorObj['vehicle'] = - `vehicle.category should be ${String(flow?.flow).toUpperCase() !== 'METRO' ? 'BUS' : 'METRO'} in Fulfillment` + `vehicle.category should be ${String(flow?.flow).toUpperCase() !== 'AIRLINE' ? 'AIRLINE' : 'AIRLINE'} in Fulfillment` } if (!fulfillment.type) { @@ -94,7 +93,7 @@ export const checkOnSelect = (data: any, msgIdSet: any, flow: { flow: string; fl if (Object.keys(getStopsError).length > 0 && Object.keys(errorValue)?.length) Object.assign(errorObj, getStopsError) - if (fulfillment.tags && String(flow?.flow).toUpperCase() !== 'METRO') { + if (fulfillment.tags && String(flow?.flow).toUpperCase() !== 'AIRLINE') { // Validate route info tags const tagsValidation = validateRouteInfoTags(fulfillment?.tags) if (!tagsValidation.isValid) { @@ -122,74 +121,48 @@ export const checkOnSelect = (data: any, msgIdSet: any, flow: { flow: string; fl } try { - onSelect.items.forEach((item: any, index: number) => { - if (!newItemIDSValue.includes(item.id)) { - const key = `item[${index}].item_id` - errorObj[key] = `/message/order/items/id in item: ${item.id} should be one of the /item/id mapped in select` - } else { - itemIdsSet.add(item.id) - } - - if (!item.descriptor || !item.descriptor.code) { - const key = `item${index}_descriptor` - errorObj[key] = `Descriptor is missing in items[${index}]` - } else { - if (!VALID_DESCRIPTOR_CODES.includes(item.descriptor.code)) { - const key = `item${index}_descriptor` - errorObj[key] = - `descriptor.code should be one of ${VALID_DESCRIPTOR_CODES} instead of ${item.descriptor.code}` - } - } - - const price = item.price - if (!price || !price.currency || !price.value) { - const key = `item${index}_price` - errorObj[key] = `Price is missing or incomplete in /items[${index}]` - } - - if (!item?.fulfillment_ids || item?.fulfillment_ids?.length === 0) { - errorObj[`invalidFulfillmentId_${index}`] = `fulfillment_ids should be present` - } else { - item.fulfillment_ids.forEach((_fulfillmentId: string) => { - // if (!storedFull.includes(fulfillmentId)) { - // errorObj[`invalidItemFulfillmentId_${index}`] = - // `Fulfillment ID '${fulfillmentId}' at index ${index} in /${constants.ON_SELECT} is not matching with the fulfillment id in previous call` - // } - }) - } - if (item?.payment_ids) { - errorObj[`payment_ids_${index}`] = `payment_ids are not part of /${constants.ON_SELECT}` + logger.info(`Comparing Items object for /${constants.SELECT} and /${constants.ON_SELECT}`); + const itemsCheck = compareItems(onSelect.items, itemIdArray) + if(!itemsCheck.success) { + Object.assign(errorObj, itemsCheck.data) } - }) + setValue(`itemIds`, Array.from(newItemIDSValue)) } catch (error: any) { logger.error(`!!Error occcurred while checking items info in /${constants.ON_SELECT}, ${error.message}`) return { error: error.message } } - - try { - logger.info(`Checking quote details in /${constants.ON_SELECT}`) - const quoteErrors = validateQuote(onSelect?.quote, constants.ON_SELECT) - Object.assign(errorObj, quoteErrors) - } catch (error: any) { - logger.error(`!!Error occcurred while checking Quote in /${constants.ON_SELECT}, ${error.message}`) - return { error: error.message } + if(onSelect?.quote) + { + try { + logger.info(`Checking quote details in /${constants.ON_SELECT}`) + const quoteCheck = validateQuotePricing(onSelect?.quote) + if(!quoteCheck.success) { + Object.assign(errorObj, quoteCheck.data) + } + const quoteErrors = validateQuote(onSelect?.quote, constants.ON_SELECT) + Object.assign(errorObj, quoteErrors) + + } catch (error: any) { + logger.error(`!!Error occcurred while checking Quote in /${constants.ON_SELECT}, ${error.message}`) + return { error: error.message } + } } if (onSelect?.payments) { errorObj[`payments`] = `payments is not part of /${constants.ON_SELECT}` } - if (!onSelect?.cancellation_terms && String(flow?.flow)?.toUpperCase() !== 'BUS') { + if (!onSelect?.cancellation_terms && String(flow?.flow)?.toUpperCase() !== 'AIRLINES') { errorObj[`cancellation_terms`] = `cancellation_terms is missing in /${constants.ON_SELECT}` } setValue(`${metroSequence.ON_SELECT}`, data) - setValue(`${metroSequence.ON_SELECT}_storedFulfillments`, Array.from(storedFull)) + setValue(`${metroSequence.ON_SELECT}_storedFulfillments`, Array.from(storedFull || [])) } catch (error: any) { logger.error(`!!Error occcurred while checking order info in /${constants.ON_SELECT}, ${error.message}`) - return { error: error.message } + return { error1: error.message } } return Object.keys(errorObj).length > 0 && errorObj diff --git a/utils/mobility/TRV12/onStatus.ts b/utils/mobility/TRV12/onStatus.ts new file mode 100644 index 00000000..81292732 --- /dev/null +++ b/utils/mobility/TRV12/onStatus.ts @@ -0,0 +1,269 @@ +/* eslint-disable no-prototype-builtins */ +// import _ from 'lodash' +import constants from '../../../constants' +import { logger } from '../../../shared/logger' +import { validateSchema, isObjectEmpty } from '../../' +import { getValue, setValue } from '../../../shared/dao' +import { + validateContext, + // validatePaymentParams, + validateQuote, + validateStops, + validateCancellationTerms, +} from '../mobilityChecks' +import { validatePaymentTags } from '../tags' +import { isEmpty } from 'lodash' +import { validateParams } from '../../metro/validate/helper' + + +const VALID_VEHICLE_CATEGORIES = ['AUTO_RICKSHAW', 'CAB', 'METRO', 'BUS', 'AIRLINE'] +const VALID_DESCRIPTOR_CODES = ['ADULT_TICKET', 'CHILD_TICKET'] +const validTypes = ['PRE-ORDER', 'ON-FULFILLMENT', 'POST-FULFILLMENT'] +const validStatus = ['NOT-PAID', 'PAID'] + +export const checkOnStatusPayload = (data: any, msgIdSet: any) => { + const errorObj: any = {} + try { + if (!data || isObjectEmpty(data)) { + return { [constants.ON_STATUS]: 'Json cannot be empty' } + } + + const { message, context }: any = data + if (!message || !context || !message.order || isObjectEmpty(message) || isObjectEmpty(message.order)) { + return { missingFields: '/context, /message, /order or /message/order is missing or empty' } + } + + const schemaValidation = validateSchema('TRV', constants.ON_STATUS, data) + + const contextRes: any = validateContext(context, msgIdSet, constants.STATUS, constants.ON_STATUS) + setValue(`${constants.ON_STATUS}_message`, message) + + if (schemaValidation !== 'error') { + Object.assign(errorObj, schemaValidation) + } + + if (!contextRes?.valid) { + Object.assign(errorObj, contextRes.ERRORS) + } + + const on_status = message.order + const itemIDS: any = getValue('ItmIDS') + const itemIdArray: any[] = [] + const fulfillmentIdsSet = new Set() + const storedFull: any = getValue(`${constants.ON_INIT}_storedFulfillments`) + + let newItemIDSValue: any[] + + if (itemIDS && itemIDS.length > 0) { + newItemIDSValue = itemIDS + } else { + on_status.items.map((item: { id: string }) => { + itemIdArray.push(item.id) + }) + newItemIDSValue = itemIdArray + } + + setValue('ItmIDS', newItemIDSValue) + + try { + logger.info(`Checking id in message object /${constants.ON_STATUS}`) + if (!on_status.id) { + errorObj.id = `Id in message object must be present/${constants.ON_STATUS}` + } else { + setValue('orderId', on_status?.id) + } + } catch (error: any) { + logger.error(`!!Error while checking id in message object /${constants.ON_STATUS}, ${error.stack}`) + } + + try { + logger.info(`Comparing provider object in /${constants.CONFIRM} and /${constants.ON_STATUS}`) + if (on_status.provider.id != getValue('providerId')) { + errorObj.prvdrId = `Provider Id mismatches in /${constants.CONFIRM} and /${constants.ON_STATUS}` + } + } catch (error: any) { + logger.error( + `!!Error while checking provider object in /${constants.CONFIRM} and /${constants.ON_STATUS}, ${error.stack}`, + ) + } + + try { + logger.info(`Validating fulfillments object for /${constants.ON_STATUS}`) + on_status.fulfillments.forEach((fulfillment: any, index: number) => { + const fulfillmentKey = `fulfillments[${index}]` + + if (!isEmpty(storedFull) && !storedFull.includes(fulfillment.id)) { + errorObj[`${fulfillmentKey}.id`] = + `/message/order/fulfillments/id in fulfillments: ${fulfillment.id} should be one of the /fulfillments/id mapped in previous call` + } else { + fulfillmentIdsSet.add(fulfillment.id) + } + + if (!fulfillment?.vehicle) { + errorObj['Vehicle'] = 'Vehicle Object Is Missing' + } else { + if (!VALID_VEHICLE_CATEGORIES.includes(fulfillment?.vehicle?.category)) { + errorObj[`${fulfillmentKey}.vehicleCategory`] = + `Vehicle category should be one of ${VALID_VEHICLE_CATEGORIES}` + } + } + + if (fulfillment.type !== 'TRIP') { + errorObj[`${fulfillmentKey}.type`] = + `Fulfillment type must be DELIVERY at index ${index} in /${constants.ON_STATUS}` + } + + // Check stops for START and END, or time range with valid timestamp and GPS + const otp = true + const cancel = false + validateStops(fulfillment?.stops, index, otp, cancel) + + // if (fulfillment.tags) { + // // Validate route info tags + // const tagsValidation = validateRouteInfoTags(fulfillment.tags) + // if (!tagsValidation.isValid) { + // Object.assign(errorObj, { tags: tagsValidation.errors }) + // } + // } + }) + } catch (error: any) { + logger.error(`!!Error occcurred while checking fulfillments info in /${constants.ON_INIT}, ${error.message}`) + return { error: error.message } + } + + try { + on_status?.items && + on_status.items.forEach((item: any, index: number) => { + if (!isEmpty(newItemIDSValue) && !newItemIDSValue.includes(item.id)) { + const key = `item[${index}].item_id` + errorObj[key] = + `/message/order/items/id in item: ${item.id} should be one of the /item/id mapped in /${constants.ON_STATUS}` + } + + if (!item.descriptor || !item.descriptor.code) { + const key = `item${index}_descriptor` + errorObj[key] = `Descriptor is missing in items[${index}]` + } else { + if (!VALID_DESCRIPTOR_CODES.includes(item.descriptor.code)) { + const key = `item${index}_descriptor` + errorObj[key] = + `descriptor.code should be one of ${VALID_DESCRIPTOR_CODES} instead of ${item.descriptor.code}` + } + } + + const price = item.price + if (!price || !price.currency || !price.value) { + const key = `item${index}_price` + errorObj[key] = `Price is incomplete in /items[${index}]` + } + + item.fulfillment_ids && + item.fulfillment_ids.forEach((fulfillmentId: string) => { + if (!isEmpty(storedFull) && !storedFull.includes(fulfillmentId)) { + errorObj[`invalidItemFulfillmentId_${index}`] = + `Fulfillment ID should be one of the fulfillment id '${fulfillmentId}' at index ${index} in /${constants.ON_STATUS} should be mapped with the ON_INIT fulfillment id.` + } + }) + }) + } catch (error: any) { + logger.error(`!!Error occcurred while checking items info in /${constants.ON_STATUS}, ${error.message}`) + return { error: error.message } + } + + try { + logger.info(`Checking payments in /${constants.ON_STATUS}`) + on_status?.payments?.forEach((arr: any, i: number) => { + if (!arr?.collected_by) { + errorObj[`payemnts[${i}]_collected_by`] = `payments.collected_by must be present in ${constants.ON_SEARCH}` + } else { + const srchCollectBy = getValue(`collected_by`) + if (srchCollectBy != arr?.collected_by) + errorObj[`payemnts[${i}]_collected_by`] = + `payments.collected_by value sent in ${constants.ON_SEARCH} should be ${srchCollectBy} as sent in ${constants.ON_STATUS}` + } + + if (!arr?.type || !validTypes.includes(arr.type)) { + errorObj[`payments[${i}]_type`] = `payments.params.type must be present in ${ + constants.ON_STATUS + } & its value must be one of: ${validTypes.join(', ')}` + } + + if (!arr?.status || !validStatus.includes(arr.status)) { + errorObj[`payments[${i}]_status`] = `payments.status must be present in ${ + constants.ON_STATUS + } & its value must be one of: ${validStatus.join(', ')}` + } + + const payment_type = getValue('INIT_PAYMENT_TYPE') ?? 'NEFT' + + const validatePayementParams = validateParams(arr.params, arr?.collected_by, constants.ON_STATUS, payment_type) + if (!isEmpty(validatePayementParams)) Object.assign(errorObj, validatePayementParams) + // const params = arr.params + // const bankCode: string | null = getValue('bank_code') + // const bankAccountNumber: string | null = getValue('bank_account_number') + // const virtualPaymentAddress: string | null = getValue('virtual_payment_address') + // // Validate bank_code + // validatePaymentParams(params, bankCode, 'bank_code', errorObj, i, constants.ON_STATUS) + + // // Validate bank_account_number + // validatePaymentParams(params, bankAccountNumber, 'bank_account_number', errorObj, i, constants.ON_STATUS) + + // // Validate virtual_payment_address + // validatePaymentParams( + // params, + // virtualPaymentAddress, + // 'virtual_payment_address', + // errorObj, + // i, + // constants.ON_STATUS, + // ) + + if (arr.time) { + if (!arr.label || arr.label !== 'INSTALLMENT') { + errorObj.time.label = `If time is present in payment, the corresponding label should be INSTALLMENT.` + } + } + + // Validate payment tags + const tagsValidation = validatePaymentTags(arr.tags, constants?.ON_STATUS) + if (!tagsValidation.isValid) { + Object.assign(errorObj, { tags: tagsValidation.errors }) + } + }) + } catch (error: any) { + logger.error(`!!Errors while checking payments in /${constants.ON_STATUS}, ${error.stack}`) + } + + try { + logger.info(`Checking quote details in /${constants.ON_STATUS}`) + const quoteErrors = validateQuote(on_status?.quote, constants.ON_STATUS) + Object.assign(errorObj, quoteErrors) + } catch (error: any) { + logger.error(`!!Error occcurred while checking Quote in /${constants.ON_STATUS}, ${error.message}`) + return { error: error.message } + } + + try { + logger.info(`Checking cancellation terms in /${constants.ON_STATUS}`) + const cancellationErrors = validateCancellationTerms(on_status.cancellation_terms, constants.ON_STATUS) + Object.assign(errorObj, cancellationErrors) + } catch (error: any) { + logger.error(`!!Error while checking cancellation_terms in /${constants.ON_STATUS}, ${error.stack}`) + } + + try { + logger.info(`Checking status in message object /${constants.ON_STATUS}`) + if (!message.order.status || !['COMPLETE', 'ACTIVE'].includes(message.order.status)) { + errorObj.status = + 'Invalid or missing"` status in the message.order object. It must be one of: COMPLETE or ACTIVE' + } + } catch (error: any) { + logger.error(`!!Error while checking status in message.order object /${constants.ON_STATUS}, ${error.stack}`) + } + + return errorObj + } catch (err: any) { + logger.error(`!!Some error occurred while checking /${constants.ON_STATUS} API`, JSON.stringify(err.stack)) + return { error: err.message } + } +} diff --git a/utils/mobility/TRV12/search.ts b/utils/mobility/TRV12/search.ts index 3eaf7dfa..866ddae7 100644 --- a/utils/mobility/TRV12/search.ts +++ b/utils/mobility/TRV12/search.ts @@ -6,7 +6,7 @@ import { validateSchema, isObjectEmpty } from '../../' import { validatePaymentTags } from './functions/validateTags' import { validateFulfillmentStops } from './functions/helper' -export const search = (data: any, msgIdSet: any, _flow: { flow: string; flowSet: string }) => { +export const search = (data: any, msgIdSet: any, flow: { flow: string; flowSet: string }) => { const errorObj: any = {} const { context, message } = data try { @@ -24,8 +24,9 @@ export const search = (data: any, msgIdSet: any, _flow: { flow: string; flowSet: errorObj['missingFields'] = '/context, /message, /intent or /message/intent is missing or empty' return Object.keys(errorObj).length > 0 && errorObj } + console.log('Flow.Flow: ', flow.flow) - const schemaValidation = validateSchema('TRV12', constants.SEARCH, data) + const schemaValidation = validateSchema(flow?.flow === 'AIRLINE' ? 'TRV12' : 'TRV12BUS', constants.SEARCH, data) const contextRes: any = validateContext(context, msgIdSet, constants.ON_SEARCH, constants.SEARCH, false) setValue(`${airlinesSequence.SEARCH}_message`, message) diff --git a/utils/mobility/TRV12/select.ts b/utils/mobility/TRV12/select.ts index 394dc6fb..1570b587 100644 --- a/utils/mobility/TRV12/select.ts +++ b/utils/mobility/TRV12/select.ts @@ -1,17 +1,10 @@ import { logger } from '../../../shared/logger' import { getValue, setValue } from '../../../shared/dao' import constants, { metroSequence } from '../../../constants' -import { - validateSchema, - isObjectEmpty, - checkGpsPrecision, - // checkMetroContext, - // checkBppIdOrBapId, - // timeDiff, -} from '../../' +import { validateSchema, isObjectEmpty, checkGpsPrecision } from '../../' import { validateContext } from '../../metro/metroChecks' -export const checkSelect = (data: any, msgIdSet: any) => { +export const checkSelect = (data: any, msgIdSet: any, flow?: any) => { if (!data || isObjectEmpty(data)) { return { [metroSequence.SELECT]: 'Json cannot be empty' } } @@ -21,8 +14,7 @@ export const checkSelect = (data: any, msgIdSet: any) => { if (!message || !context || !message.order || isObjectEmpty(message) || isObjectEmpty(message.order)) { return { missingFields: '/context, /message, /order or /message/order is missing or empty' } } - - const schemaValidation = validateSchema('TRV12', constants.SELECT, data) + const schemaValidation = validateSchema(flow?.flow === 'Airlines' ? 'TRV12' : 'TRV12BUS', constants.SELECT, data) const contextRes: any = validateContext(context, msgIdSet, constants.ON_SEARCH, constants.SELECT) setValue(`${metroSequence.SELECT}_message`, message) @@ -58,26 +50,51 @@ export const checkSelect = (data: any, msgIdSet: any) => { } try { - logger.info(`Comparing Items object for /${constants.ON_SEARCH} and /${constants.SELECT}`) - + logger.info(`Comparing Items object for /${constants.ON_SEARCH} and /${constants.SELECT}`); + + const parentItems: Record = {}; + const childItems: Record = {}; select?.items?.forEach((item: any, index: number) => { - if (storedItemIDS && !storedItemIDS.includes(item.id)) { - const key = `item[${index}].item_id` - errorObj[key] = - `/message/order/items/id in item: ${item.id} should be one of the /item/id mapped in previous call` - } else { - setValue('itemId', item.id) - setValue(`qunatity_count`, item?.quantity?.count || 0) + const itemId = item.id; + const parentItemId = item.parent_item_id; + const itemCount = item?.quantity?.selected?.count || 0; + + if (itemId) { + parentItems[itemId] = itemCount; + } else if (parentItemId) { + if (itemCount !== 1){ + errorObj[`item[${parentItemId}].quantity_count`] = + `Parent item ${parentItemId} should have total selected count of 1`; + } + childItems[parentItemId] = (childItems[parentItemId] || 0) + 1; } - }) + + if (storedItemIDS && !storedItemIDS.includes(itemId)) { + errorObj[`item[${index}].item_id`] = + `/message/order/items/id in item: ${itemId} should be one of the /item/id mapped in previous call`; + } + }); + + Object.keys(childItems).forEach((parentId) => { + if (parentItems[parentId] !== childItems[parentId]) { + errorObj[`item[${parentId}].quantity_count`] = + `Parent item ${parentId} has total count ${parentItems[parentId]}, but child items sum up to ${childItems[parentId]}`; + } + }); + console.log('Parent Items: ',parentItems) + console.log('Child Items: ',childItems) + } catch (error: any) { logger.error( `!!Error while Comparing and Mapping Items in /${constants.ON_SEARCH} and /${constants.SELECT}, ${error.stack}`, - ) + ); } + if ('fulfillments' in message.order && Array.isArray(message.order.fulfillments)) { const fulfillmentErrors: any = {} + + let childFulfilmentCount = 0; message.order.fulfillments.forEach((fulfillment: any, index: number) => { const fulfillmentKey = `fulfillments[${index}]` @@ -87,18 +104,26 @@ export const checkSelect = (data: any, msgIdSet: any) => { } if (fulfillment.type && fulfillment.type !== 'DELIVERY') { - fulfillmentErrors[`${fulfillmentKey}.type`] = `Fulfillment type should be 'DELIVERY' in provoider}` + fulfillmentErrors[`${fulfillmentKey}.type`] = `Fulfillment type should be 'DELIVERY' in provider}` + } + // to be cross checked against DB count + if(!fulfillment.stops){ + childFulfilmentCount++; } + if(childFulfilmentCount){ + console.log(childFulfilmentCount) + } if ('stops' in fulfillment && Array.isArray(fulfillment.stops)) { fulfillment.stops.forEach((stop: any, stopIndex: number) => { - const stopKey = `${fulfillmentKey}.stops[${stopIndex}]` + + const stopKey = `${fulfillmentKey}.stops[${stopIndex}]` if (!stop.type) { fulfillmentErrors[`${stopKey}.type`] = `${stopKey}/type is required` } - if (checkGpsPrecision(stop?.location.gps)) { + if (stop?.location?.gps && checkGpsPrecision(stop?.location.gps)) { fulfillmentErrors[`${stopKey}.gps`] = 'gps must be specified with at least six decimal places of precision.' } diff --git a/utils/mobility/TRV12/status.ts b/utils/mobility/TRV12/status.ts new file mode 100644 index 00000000..1058ef95 --- /dev/null +++ b/utils/mobility/TRV12/status.ts @@ -0,0 +1,47 @@ +import { logger } from '../../../shared/logger' +import { getValue, setValue } from '../../../shared/dao' +import constants, { airlinesSequence } from '../../../constants' +import { validateSchema, isObjectEmpty } from '../../' +import { validateContext } from '../../metro/metroChecks' + +export const checkStatusPayload = (data: any, msgIdSet: any) => { + const errorObj: any = {} + const orderId = getValue('orderId') + try { + if (!data || isObjectEmpty(data)) { + return { [constants.CANCEL]: 'Json cannot be empty' } + } + + const { message, context }: any = data + if (!message || !context || isObjectEmpty(message)) { + return { missingFields: '/context, /message, /message/order ID is missing or empty' } + } + + const schemaValidation = validateSchema('TRV12', constants.CANCEL, data) + const contextRes: any = validateContext( + context, + msgIdSet, + airlinesSequence?.ON_CANCEL, + constants.STATUS + ) + setValue(`${constants.STATUS}_message`, message) + + if (schemaValidation !== 'error') { + Object.assign(errorObj, schemaValidation) + } + + if (!contextRes?.valid) { + Object.assign(errorObj, contextRes.ERRORS) + } + + if (!message?.order_id) errorObj['order_id'] = `order_id should be sent in /${constants.STATUS}` + else { + if (message?.order_id !== orderId) errorObj['order_id'] = `order_id should be same as in /${constants.ON_CONFIRM}` + } + + return errorObj + } catch (err: any) { + logger.error(`!!Some error occurred while checking /${constants.INIT} API`, err) + return { error: err.message } + } +}