Skip to content

Commit

Permalink
feat: Implement yymmdd to yyyymmdd Date Conversion Method and Monorep…
Browse files Browse the repository at this point in the history
…o Testing Structure (#53)
  • Loading branch information
lotharking authored Dec 6, 2024
1 parent 486406c commit 27c0a5f
Show file tree
Hide file tree
Showing 14 changed files with 214 additions and 24 deletions.
19 changes: 19 additions & 0 deletions jest.config.base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Config } from '@jest/types'

const config: Config.InitialOptions = {
preset: 'ts-jest',
testEnvironment: 'node',
coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'],
coverageDirectory: '<rootDir>/coverage/',
testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'],
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
isolatedModules: true,
},
],
},
}

export default config
11 changes: 11 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Config } from '@jest/types'

import base from './jest.config.base'

const config: Config.InitialOptions = {
...base,
roots: ['<rootDir>'],
projects: ['<rootDir>/packages/*'],
}

export default config
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"check-types:build": "yarn workspaces run tsc --noEmit -p tsconfig.build.json",
"format": "prettier \"packages/*/src/**/*.ts\" --write",
"check-format": "prettier -c \"packages/*/src/**/*.ts\"",
"test": "yarn workspaces run test",
"test": "yarn workspaces run test --passWithNoTests",
"lint": "eslint \"{packages,apps,libs}/**/*.ts\" --fix",
"validate": "yarn lint && yarn check-types && yarn check-format"
},
Expand Down
12 changes: 12 additions & 0 deletions packages/client/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Config } from '@jest/types'

import base from '../../jest.config.base'

import packageJson from './package.json'

const config: Config.InitialOptions = {
...base,
displayName: packageJson.name,
}

export default config
12 changes: 12 additions & 0 deletions packages/main/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Config } from '@jest/types'

import base from '../../jest.config.base'

import packageJson from './package.json'

const config: Config.InitialOptions = {
...base,
displayName: packageJson.name,
}

export default config
12 changes: 12 additions & 0 deletions packages/model/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Config } from '@jest/types'

import base from '../../jest.config.base'

import packageJson from './package.json'

const config: Config.InitialOptions = {
...base,
displayName: packageJson.name,
}

export default config
1 change: 1 addition & 0 deletions packages/model/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './messages'
export * from './events'
export * from './types'
export * from './utils'
15 changes: 9 additions & 6 deletions packages/model/src/messages/mrtd/EMrtdDataSubmitMessage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EMrtdData } from '@2060.io/credo-ts-didcomm-mrtd'
import * as Mrz from 'mrz'

import { convertShortDate } from '../../utils'
import { BaseMessage, BaseMessageOptions } from '../BaseMessage'
import { MessageType } from '../MessageType'

Expand Down Expand Up @@ -87,16 +88,21 @@ export class EMrtdDataSubmitMessage extends BaseMessage {
}
})(mrzString.length)
const parsedMrz = Mrz.parse(formattedMrz)
const rawSex = parsedMrz.fields.sex ?? undefined
const birthDateFromAdditionalPersonalData = parsed.fields.additionalPersonalData?.fullDateOfBirth
const dateOfBirth =
!birthDateFromAdditionalPersonalData || birthDateFromAdditionalPersonalData.toString().length < 6
? parsedMrz.fields.birthDate
: birthDateFromAdditionalPersonalData?.toString()

const newEmrtdData: EMrtdRawData = {
raw: raw,
processed: {
documentType: parsedMrz.format,
documentNumber: parsedMrz.documentNumber ?? undefined,
issuingState: parsedMrz.fields.issuingState ?? undefined,
dateOfExpiry: parsedMrz.fields.expirationDate ?? undefined, // TODO: Check and specify date format
sex: parsedMrz.fields.sex ?? undefined,
dateOfExpiry: convertShortDate(parsedMrz.fields.expirationDate, true),
sex: rawSex ? rawSex.charAt(0).toUpperCase() : undefined,
nationality: parsedMrz.fields.nationality ?? undefined,
lastName: parsedMrz.fields.lastName ?? undefined,
firstName: parsedMrz.fields.firstName ?? undefined,
Expand All @@ -105,10 +111,7 @@ export class EMrtdDataSubmitMessage extends BaseMessage {
nameOfHolder:
parsed.fields.additionalPersonalData?.nameOfHolder ??
`${parsedMrz.fields.lastName} ${parsedMrz.fields.firstName}`,
dateOfBirth:
birthDateFromAdditionalPersonalData && birthDateFromAdditionalPersonalData !== 0
? birthDateFromAdditionalPersonalData.toString().slice(2)
: (parsedMrz.fields.birthDate ?? undefined), // TODO: Check and specify date format
dateOfBirth: convertShortDate(dateOfBirth, false),
otherNames: parsed.fields.additionalPersonalData?.otherNames,
personalNumber: parsed.fields.additionalPersonalData?.personalNumber,
placeOfBirth: parsed.fields.additionalPersonalData?.placeOfBirth,
Expand Down
48 changes: 48 additions & 0 deletions packages/model/src/utils/dateUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Converts a Machine Readable Travel Document (MRTD) date in the format `YYMMDD` to a complete
* `YYYYMMDD` date format, taking into account the current century.
*
* **Note:** This method is limited to interpreting dates based on the current year.
* It may not handle dates correctly for years beyond the range determined by the
* current century (e.g., for dates after 2050 when the current year is in the 21st century).
*
* @param {string} date - The MRTD date string in the format `YYMMDD`.
* @param {boolean} isExpirationDate - A boolean flag indicating whether the date is an expiration date.
* @returns {string} - The converted date in the format `YYYYMMDD`, or the original input
* if the input is not a valid `YYMMDD` date.
*
* @example
* // Current year: 2024
* convertMRTDDate("240101"); // Returns "20240101"
* convertMRTDDate("991231"); // Returns "19991231"
* convertMRTDDate("abcd12"); // Returns "abcd12" (invalid input)
*/
export function convertShortDate(date: string | null | undefined, isExpirationDate: boolean) {
if (!date || !/^\d{6}$/.test(date)) return date ?? undefined

const currentYear = new Date().getFullYear()
const currentCentury = Math.floor(currentYear / 100)
const year = parseInt(date.slice(0, 2), 10)
const month = date.slice(2, 4)
const day = date.slice(4, 6)

let fullYear: number

if (isExpirationDate) {
if (year <= currentYear % 100) {
fullYear = currentCentury * 100 + year
if (fullYear < currentYear) {
fullYear += 100
}
} else {
fullYear = currentCentury * 100 + year
}
} else {
if (year <= currentYear % 100) {
fullYear = currentCentury * 100 + year
} else {
fullYear = (currentCentury - 1) * 100 + year
}
}
return `${fullYear}${month}${day}`
}
1 change: 1 addition & 0 deletions packages/model/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dateUtils'
60 changes: 60 additions & 0 deletions packages/model/tests/DidCommMrtdModel.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
DecodedAdditionalPersonalData,
DecodedImage,
DecodedSecurtyObjectOfDocument,
} from '@li0ard/tsemrtd/dist/consts/interfaces'

import { EMrtdDataSubmitMessage, MrtdSubmitState } from '../src/messages'

describe('EMrtdDataSubmitMessage', () => {
let securityObjectOfDocument: DecodedSecurtyObjectOfDocument
let additionalPersonalData: DecodedAdditionalPersonalData
let image: DecodedImage
it('should initialize with correct type and state', () => {
const rawMock = { someKey: 'someValue' }
const parsedMock = {
fields: {
com: {
ldsVersion: '1.7',
unicodeVersion: '9.0',
tags: Buffer.from('tagsData'),
},
mrzData: 'P<COLDOE<<JOHN<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<BC123456<2COL9001011M3005103CC12345678<<<<04',
images: [
{
...image,
imageType: 1,
imageData: Buffer.from('faceImage1'),
},
],
additionalPersonalData: {
...additionalPersonalData,
nameOfHolder: 'John Doe',
fullDateOfBirth: 19900101,
},
securityObjectOfDocument,
},
valid: true,
}

const message = new EMrtdDataSubmitMessage({
id: '123',
threadId: '456',
timestamp: new Date(),
connectionId: 'conn-1',
state: MrtdSubmitState.Submitted,
dataGroups: { raw: rawMock, parsed: parsedMock },
})

expect(message.dataGroups?.processed.documentType).toBe('TD3')
expect(message.dataGroups?.processed.documentNumber).toBe('BC123456')
expect(message.dataGroups?.processed.lastName).toBe('DOE')
expect(message.dataGroups?.processed.firstName).toBe('JOHN')
expect(message.dataGroups?.processed.dateOfBirth).toBe('19900101')
expect(message.dataGroups?.processed.dateOfExpiry).toBe('20300510')
expect(message.dataGroups?.processed.sex).toBe('M')
expect(message.dataGroups?.processed.nationality).toBe('COL')
expect(message.dataGroups?.processed.nameOfHolder).toBe('John Doe')
expect(message.dataGroups?.processed.faceImages[0]).toContain('data:image/jp2;base64,')
})
})
12 changes: 12 additions & 0 deletions packages/nestjs-client/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Config } from '@jest/types'

import base from '../../jest.config.base'

import packageJson from './package.json'

const config: Config.InitialOptions = {
...base,
displayName: packageJson.name,
}

export default config
17 changes: 0 additions & 17 deletions packages/nestjs-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,5 @@
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
16 changes: 16 additions & 0 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.build.json",
"compilerOptions": {
"lib": ["ES2021.Promise"],
"baseUrl": ".",
"paths": {
"@2060.io/service-agent-client": ["packages/client/src"],
"@2060.io/service-agent-main": ["packages/main/src"],
"@2060.io/service-agent-model": ["packages/model/src"],
"@2060.io/service-agent-nestjs-client": ["packages/nestjs-client/src"],
},
"types": ["jest", "node"]
},
"include": ["tests", "samples", "**/tests", "**/samples"],
"exclude": ["node_modules", "build", "**/build/**"]
}

0 comments on commit 27c0a5f

Please sign in to comment.