Skip to content

Commit

Permalink
Fix/validation for birthdate (#27)
Browse files Browse the repository at this point in the history
* fix: add validation for birthdate

* fix: change error code for birthdate validation

* fix: wrong place of BirthDate validation

* feat: add tests for BirthDate validation

* refactor: move tests to another location

* fix: error in description of tests

* feat: add cross-component validation test for BirthDate

* refactor: use Luxon for isInLeapYear function

* refactor: use Luxon for validation of BirthDate

* chore: bump version of content and runtime

* refactor: use inline Error message instead of ValidationError

* feat: add ValidationErrorWithoutProperty class

* fix: add validation to prevent the creation of BirthDates in the future

* refactor: move tests from runtime to content

* refactor: move files back

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
britsta and mergify[bot] authored Feb 19, 2024
1 parent 422e358 commit 5242e14
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 7 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion packages/content/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nmshd/content",
"version": "2.8.5",
"version": "2.8.6",
"description": "The content library defines data structures that can be transmitted using the transport library.",
"homepage": "https://enmeshed.eu",
"repository": {
Expand Down
9 changes: 9 additions & 0 deletions packages/content/src/ValidationErrorWithoutProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ValidationError } from "@js-soft/ts-serval";

export class ValidationErrorWithoutProperty extends ValidationError {
public constructor(type: string, reason: string, cause?: Error) {
super(type, "n/a", reason, cause);

this.message = `${type} :: ${reason}`;
}
}
20 changes: 19 additions & 1 deletion packages/content/src/attributes/types/birth/BirthDate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { serialize, type, validate } from "@js-soft/ts-serval";
import { Serializable, serialize, type, validate } from "@js-soft/ts-serval";
import nameOf from "easy-tsnameof";
import { DateTime } from "luxon";
import { ValidationErrorWithoutProperty } from "../../../ValidationErrorWithoutProperty";
import { AbstractAttributeValue } from "../../AbstractAttributeValue";
import { AbstractComplexValue, AbstractComplexValueJSON, IAbstractComplexValue } from "../../AbstractComplexValue";
import { RenderHints, ValueHints } from "../../hints";
Expand Down Expand Up @@ -37,6 +38,23 @@ export class BirthDate extends AbstractComplexValue implements IBirthDate {
@validate()
public year: BirthYear;

protected static override postFrom<T extends Serializable>(value: T): T {
if (!(value instanceof BirthDate)) throw new Error("this should never happen");

const dateTime = DateTime.fromObject({ day: value.day.value, month: value.month.value, year: value.year.value });
const isValid = dateTime.isValid;

if (!isValid) {
throw new ValidationErrorWithoutProperty(BirthDate.name, "The BirthDate is not a valid date.");
}

if (DateTime.utc() < dateTime) {
throw new ValidationErrorWithoutProperty(BirthDate.name, "You cannot enter a BirthDate that is in the future.");
}

return value;
}

public static get valueHints(): ValueHints {
return ValueHints.from({
propertyHints: {
Expand Down
47 changes: 47 additions & 0 deletions packages/content/test/attributes/BirthDate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { UserfriendlyApplicationError } from "@nmshd/app-runtime/src/UserfriendlyApplicationError";
import { BirthDate } from "@nmshd/content";
import { ValidationErrorWithoutProperty } from "@nmshd/content/src/ValidationErrorWithoutProperty";
import { DateTime } from "luxon";

describe("creation of RepositoryAttributes of Attribute Value Type BirthDate", () => {
test("can create a RepositoryAttribute of Attribute Value Type BirthDate", function () {
const validBirthDate = BirthDate.from({ day: 1, month: 12, year: 1990 });
expect(validBirthDate.constructor.name).toBe("BirthDate");
expect(validBirthDate.day.value).toBe(1);
expect(validBirthDate.month.value).toBe(12);
expect(validBirthDate.year.value).toBe(1990);
});

test("returns an error when trying to create an invalid BirthDate with violated validation criteria of a single property", function () {
const invalidBirthDateCall = () => {
BirthDate.from({ day: 1, month: 13, year: 1990 });
};
expect(invalidBirthDateCall).toThrow(
new UserfriendlyApplicationError("error.runtime.requestDeserialization", "BirthMonth.value:Number :: must be an integer value between 1 and 12")
);
});

test("returns an error when trying to create an invalid BirthDate with cross-component violated validation criteria for June", function () {
const invalidBirthDateCall = () => {
BirthDate.from({ day: 31, month: 6, year: 1990 });
};
expect(invalidBirthDateCall).toThrow(new ValidationErrorWithoutProperty(BirthDate.name, "The BirthDate is not a valid date."));
});

test("returns an error when trying to create an invalid BirthDate with cross-component violated validation criteria for February", function () {
const invalidBirthDateCall = () => {
BirthDate.from({ day: 29, month: 2, year: 2010 });
};
expect(invalidBirthDateCall).toThrow(new ValidationErrorWithoutProperty(BirthDate.name, "The BirthDate is not a valid date."));
});

test("returns an error when trying to create an BirthDate that is in the future", function () {
const currentDateTime = DateTime.utc();
const yearInFuture = currentDateTime.year + 1;

const invalidBirthDateCall = () => {
BirthDate.from({ day: 10, month: 6, year: yearInFuture });
};
expect(invalidBirthDateCall).toThrow(new ValidationErrorWithoutProperty(BirthDate.name, "You cannot enter a BirthDate that is in the future."));
});
});
4 changes: 2 additions & 2 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nmshd/runtime",
"version": "4.1.2",
"version": "4.1.3",
"description": "The enmeshed client runtime.",
"homepage": "https://enmeshed.eu",
"repository": {
Expand Down Expand Up @@ -65,7 +65,7 @@
"@js-soft/ts-serval": "2.0.10",
"@js-soft/ts-utils": "^2.3.3",
"@nmshd/consumption": "3.9.3",
"@nmshd/content": "2.8.5",
"@nmshd/content": "2.8.6",
"@nmshd/crypto": "2.0.6",
"@nmshd/transport": "2.3.1",
"ajv": "^8.12.0",
Expand Down

0 comments on commit 5242e14

Please sign in to comment.