diff --git a/apps/server/package.json b/apps/server/package.json index 84aa33f..c5492d2 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,7 +1,7 @@ { "name": "@overture-stack/lectern-server", "private": true, - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "Overture Data Dictionary Management", "scripts": { "build": "tsc -p tsconfig.build.json", diff --git a/package.json b/package.json index 47b1a45..3e5bdbc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@overture-stack/lectern", "private": true, - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "Schema Manager and Validation for Data Dictionaries", "scripts": { "build:all": "pnpm nx run-many --all --target=build", diff --git a/packages/client/package.json b/packages/client/package.json index cc467ae..2df0950 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@overture-stack/lectern-client", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "files": [ "dist/" ], diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index ce41fb2..23d8c98 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -45,6 +45,7 @@ export type { ParseDictionaryData, ParseDictionaryFailure, ParseDictionaryResult, + ParseFieldError, ParseSchemaError, ParseSchemaFailureData, ParseSchemaResult, diff --git a/packages/dictionary/package.json b/packages/dictionary/package.json index 99e3db7..08f3ee2 100644 --- a/packages/dictionary/package.json +++ b/packages/dictionary/package.json @@ -1,6 +1,6 @@ { "name": "@overture-stack/lectern-dictionary", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "", "files": [ "dist/" diff --git a/packages/validation/package.json b/packages/validation/package.json index 756b5d8..0774a49 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -1,6 +1,6 @@ { "name": "@overture-stack/lectern-validation", - "version": "2.0.0-beta.2", + "version": "2.0.0-beta.3", "description": "Logic for validating data using a Lectern dictionary", "files": [ "dist/" diff --git a/packages/validation/src/parseValues/matchCodeListFormatting.ts b/packages/validation/src/parseValues/matchCodeListFormatting.ts index 9553686..03ffe55 100644 --- a/packages/validation/src/parseValues/matchCodeListFormatting.ts +++ b/packages/validation/src/parseValues/matchCodeListFormatting.ts @@ -29,7 +29,7 @@ const collectAllNestedCodeLists = ( return TypeUtils.asArray(restrictions).flatMap((restrictionsObject) => { if ('if' in restrictionsObject) { const thenCodeLists = restrictionsObject.then ? collectAllNestedCodeLists(restrictionsObject.then) : []; - const elseCodeLists = restrictionsObject.else ? collectAllNestedCodeLists(restrictionsObject) : []; + const elseCodeLists = restrictionsObject.else ? collectAllNestedCodeLists(restrictionsObject.else) : []; return [...thenCodeLists, ...elseCodeLists]; } else { return restrictionsObject.codeList ? restrictionsObject.codeList : []; diff --git a/packages/validation/test/fixtures/fields/conditionalRestrictions/fieldStringConditionalExistsWithoutThenElse.ts b/packages/validation/test/fixtures/fields/conditionalRestrictions/fieldStringConditionalExistsWithoutThenElse.ts new file mode 100644 index 0000000..3943ac3 --- /dev/null +++ b/packages/validation/test/fixtures/fields/conditionalRestrictions/fieldStringConditionalExistsWithoutThenElse.ts @@ -0,0 +1,27 @@ +import { SchemaField, type SchemaStringField } from '@overture-stack/lectern-dictionary'; +import { fieldStringNoRestriction } from '../noRestrictions/fieldStringNoRestriction'; +import { validateFixture } from '../../../testUtils/validateFixture'; + +export const fieldStringConditionalExistsWithouthThenElse = { + name: 'conditional-field', + valueType: 'string', + description: 'Required if `fieldStringNoRestriction` field exists, otherwise must be empty', + restrictions: { + if: { + conditions: [ + { + fields: [fieldStringNoRestriction.name], + match: { + exists: true, + }, + }, + ], + }, + }, +} as const satisfies SchemaStringField; + +validateFixture( + fieldStringConditionalExistsWithouthThenElse, + SchemaField, + 'fieldStringConditionalExistsWithouthThenElse is not a valid SchemaField', +); diff --git a/packages/validation/test/parseValues/parseField.spec.ts b/packages/validation/test/parseValues/parseField.spec.ts index d89101f..df7c5b1 100644 --- a/packages/validation/test/parseValues/parseField.spec.ts +++ b/packages/validation/test/parseValues/parseField.spec.ts @@ -30,6 +30,8 @@ import { fieldBooleanArrayRequired } from '../fixtures/fields/simpleRestrictions import { fieldStringCodeList } from '../fixtures/fields/simpleRestrictions/string/fieldStringCodeList'; import { codeListString } from '../fixtures/restrictions/codeListsFixtures'; import { fieldStringArrayCodeList } from '../fixtures/fields/simpleRestrictions/string/fieldStringArrayCodeList'; +import { fieldStringConditionalExists } from '../fixtures/fields/conditionalRestrictions/fieldStringConditionalExists'; +import { fieldStringConditionalExistsWithouthThenElse } from '../fixtures/fields/conditionalRestrictions/fieldStringConditionalExistsWithoutThenElse'; describe('Parse Values - parseFieldValue', () => { describe('Single Value Fields', () => { @@ -165,6 +167,34 @@ describe('Parse Values - parseFieldValue', () => { expect(parseFieldValue(' !@#$%^&* ()_+ ', fieldStringNoRestriction).success).true; expect(parseFieldValue(' !@#$%^&* ()_+ ', fieldStringNoRestriction).data).equals('!@#$%^&* ()_+'); }); + it('Successfuly parses strings, with conditional restrictions', () => { + const value = 'any random string value!!!'; + const result = parseFieldValue(value, fieldStringConditionalExists); + expect(result.success).true; + expect(result.data).equal(value); + + expect(parseFieldValue(' 123', fieldStringConditionalExists).success).true; + expect(parseFieldValue(' 123', fieldStringConditionalExists).data).equals('123'); + expect(parseFieldValue('false ', fieldStringConditionalExists).success).true; + expect(parseFieldValue('false ', fieldStringConditionalExists).data).equals('false'); + expect(parseFieldValue(' !@#$%^&* ()_+ ', fieldStringConditionalExists).success).true; + expect(parseFieldValue(' !@#$%^&* ()_+ ', fieldStringConditionalExists).data).equals('!@#$%^&* ()_+'); + }); + it('Successfuly parses strings, with conditional restrictions without then or else', () => { + const value = 'any random string value!!!'; + const result = parseFieldValue(value, fieldStringConditionalExistsWithouthThenElse); + expect(result.success).true; + expect(result.data).equal(value); + + expect(parseFieldValue(' 123', fieldStringConditionalExistsWithouthThenElse).success).true; + expect(parseFieldValue(' 123', fieldStringConditionalExistsWithouthThenElse).data).equals('123'); + expect(parseFieldValue('false ', fieldStringConditionalExistsWithouthThenElse).success).true; + expect(parseFieldValue('false ', fieldStringConditionalExistsWithouthThenElse).data).equals('false'); + expect(parseFieldValue(' !@#$%^&* ()_+ ', fieldStringConditionalExistsWithouthThenElse).success).true; + expect(parseFieldValue(' !@#$%^&* ()_+ ', fieldStringConditionalExistsWithouthThenElse).data).equals( + '!@#$%^&* ()_+', + ); + }); it('Updates string to match formatting of codeList value', () => { const value = 'banana'; const result = parseFieldValue(value, fieldStringCodeList); diff --git a/packages/validation/test/validateField/resolveFieldRestrictions.spec.ts b/packages/validation/test/validateField/resolveFieldRestrictions.spec.ts index 1f460dc..fb3f39f 100644 --- a/packages/validation/test/validateField/resolveFieldRestrictions.spec.ts +++ b/packages/validation/test/validateField/resolveFieldRestrictions.spec.ts @@ -33,6 +33,7 @@ import { fieldBooleanNoRestriction } from '../fixtures/fields/noRestrictions/fie import { fieldNumberNoRestriction } from '../fixtures/fields/noRestrictions/fieldNumberNoRestriction'; import { fieldStringNoRestriction } from '../fixtures/fields/noRestrictions/fieldStringNoRestriction'; import { regexAlphaOnly } from '../fixtures/restrictions/regexFixtures'; +import { fieldStringConditionalExistsWithouthThenElse } from '../fixtures/fields/conditionalRestrictions/fieldStringConditionalExistsWithoutThenElse'; describe('Field - resolveFieldRestrictions', () => { it('Returns empty array when there are no restrictions', () => { @@ -103,6 +104,18 @@ describe('Field - resolveFieldRestrictions', () => { const codeListRestriction = restrictions.find((restriction) => restriction.type === 'codeList'); expect(codeListRestriction).not.undefined; }); + it('Does not add any restriction when condition is true and there is no `then`', () => { + const record: DataRecord = { + [fieldStringNoRestriction.name]: 'anything', + [fieldStringConditionalExistsWithouthThenElse.name]: 'anything goes', + }; + const restrictions = resolveFieldRestrictions( + record[fieldStringConditionalExistsWithouthThenElse.name], + record, + fieldStringConditionalExistsWithouthThenElse, + ); + expect(restrictions.length).equal(0); + }); it('Does not add any restriction when condition fails and there is no `else`', () => { const record: DataRecord = { [fieldNumberNoRestriction.name]: 0,