Skip to content

Commit ceba6f3

Browse files
authored
Merge pull request #161 from IQSS/fix/157-date-format-validation
Fix date format validation on create & update dataset
2 parents d61739b + 217cc59 commit ceba6f3

File tree

4 files changed

+106
-11
lines changed

4 files changed

+106
-11
lines changed

src/datasets/domain/useCases/validators/SingleMetadataFieldValidator.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import { DateFormatFieldError } from './errors/DateFormatFieldError'
77
import { MetadataFieldValidator } from './MetadataFieldValidator'
88
import { DatasetMetadataChildFieldValueDTO } from '../../dtos/DatasetDTO'
99
import { MultipleMetadataFieldValidator } from './MultipleMetadataFieldValidator'
10-
import { MetadataFieldType } from '../../../../metadataBlocks/domain/models/MetadataBlock'
10+
import {
11+
MetadataFieldType,
12+
MetadataFieldWatermark
13+
} from '../../../../metadataBlocks/domain/models/MetadataBlock'
1114

1215
export class SingleMetadataFieldValidator extends BaseMetadataFieldValidator {
1316
validate(datasetMetadataFieldAndValueInfo: DatasetMetadataFieldAndValueInfo): void {
@@ -75,11 +78,38 @@ export class SingleMetadataFieldValidator extends BaseMetadataFieldValidator {
7578
private validateDateFieldValue(
7679
datasetMetadataFieldAndValueInfo: DatasetMetadataFieldAndValueInfo
7780
) {
78-
const dateFormatRegex = /^\d{4}-\d{2}-\d{2}$/
79-
if (!dateFormatRegex.test(datasetMetadataFieldAndValueInfo.metadataFieldValue as string)) {
81+
const {
82+
metadataFieldInfo: { watermark },
83+
metadataFieldValue
84+
} = datasetMetadataFieldAndValueInfo
85+
86+
const acceptsAllDateFormats = watermark === MetadataFieldWatermark.YYYYOrYYYYMMOrYYYYMMDD
87+
88+
const YYYY_MM_DD_DATE_FORMAT_REGEX = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/
89+
90+
const YYYY_MM_FORMAT_REGEX = /^\d{4}-(0[1-9]|1[0-2])$/
91+
92+
const YYYY_FORMAT_REGEX = /^\d{4}$/
93+
94+
const isValidDateFormat = (value: string): boolean => {
95+
if (acceptsAllDateFormats) {
96+
// Check if it matches any of the formats
97+
return (
98+
YYYY_MM_DD_DATE_FORMAT_REGEX.test(value) ||
99+
YYYY_MM_FORMAT_REGEX.test(value) ||
100+
YYYY_FORMAT_REGEX.test(value)
101+
)
102+
} else {
103+
// Only accepts YYYY-MM-DD format
104+
return YYYY_MM_DD_DATE_FORMAT_REGEX.test(value)
105+
}
106+
}
107+
108+
if (!isValidDateFormat(metadataFieldValue as string)) {
80109
throw new DateFormatFieldError(
81110
datasetMetadataFieldAndValueInfo.metadataFieldKey,
82111
datasetMetadataFieldAndValueInfo.metadataBlockName,
112+
watermark,
83113
datasetMetadataFieldAndValueInfo.metadataParentFieldKey,
84114
datasetMetadataFieldAndValueInfo.metadataFieldPosition
85115
)

src/datasets/domain/useCases/validators/errors/DateFormatFieldError.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export class DateFormatFieldError extends FieldValidationError {
44
constructor(
55
metadataFieldName: string,
66
citationBlockName: string,
7+
validDateFormat: string,
78
parentMetadataFieldName?: string,
89
fieldPosition?: number
910
) {
@@ -12,7 +13,7 @@ export class DateFormatFieldError extends FieldValidationError {
1213
citationBlockName,
1314
parentMetadataFieldName,
1415
fieldPosition,
15-
'The field requires a valid date format (YYYY-MM-DD).'
16+
`The field requires a valid date format (${validDateFormat}).`
1617
)
1718
}
1819
}

test/testHelpers/datasets/datasetHelper.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,8 @@ export const createDatasetDTO = (
360360
alternativeRequiredTitleValue?: DatasetMetadataFieldValueDTO,
361361
timePeriodCoveredStartValue?: DatasetMetadataFieldValueDTO,
362362
contributorTypeValue?: DatasetMetadataFieldValueDTO,
363-
license?: DatasetLicense
363+
license?: DatasetLicense,
364+
dateOfCreationValue?: DatasetMetadataFieldValueDTO
364365
): DatasetDTO => {
365366
const validTitle = 'test dataset'
366367
const validAuthorFieldValue = [
@@ -389,6 +390,9 @@ export const createDatasetDTO = (
389390
...(timePeriodCoveredStartValue && {
390391
timePeriodCoveredStart: timePeriodCoveredStartValue
391392
}),
393+
...(dateOfCreationValue && {
394+
dateOfCreation: dateOfCreationValue
395+
}),
392396
...(contributorTypeValue && {
393397
contributor: [
394398
{
@@ -536,7 +540,7 @@ export const createDatasetMetadataBlockModel = (): MetadataBlock => {
536540
displayName: 'Time Period Start Date',
537541
title: 'Start Date',
538542
type: MetadataFieldType.Date,
539-
watermark: MetadataFieldWatermark.Empty,
543+
watermark: MetadataFieldWatermark.YYYYOrYYYYMMOrYYYYMMDD,
540544
description: 'The start date of the time period that the data refer to',
541545
multiple: false,
542546
isControlledVocabulary: false,
@@ -546,6 +550,21 @@ export const createDatasetMetadataBlockModel = (): MetadataBlock => {
546550
displayOnCreate: true,
547551
typeClass: MetadataFieldTypeClass.Primitive
548552
},
553+
dateOfCreation: {
554+
name: 'dateOfCreation',
555+
displayName: 'Date of Creation',
556+
title: 'Creation Date',
557+
type: MetadataFieldType.Date,
558+
watermark: MetadataFieldWatermark.YyyyMmDD,
559+
description: 'The creation date of dataset',
560+
multiple: false,
561+
isControlledVocabulary: false,
562+
displayFormat: '#NAME: #VALUE ',
563+
isRequired: false,
564+
displayOrder: 5,
565+
displayOnCreate: true,
566+
typeClass: MetadataFieldTypeClass.Primitive
567+
},
549568
contributor: {
550569
name: 'contributor',
551570
displayName: 'Contributor',

test/unit/datasets/DatasetResourceValidator.test.ts

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import {
55
createDatasetDTOWithoutFirstLevelRequiredField
66
} from '../../testHelpers/datasets/datasetHelper'
77
import { FieldValidationError } from '../../../src/datasets/domain/useCases/validators/errors/FieldValidationError'
8-
import { DatasetDTO } from '../../../src/datasets/domain/dtos/DatasetDTO'
8+
import {
9+
DatasetDTO,
10+
DatasetMetadataChildFieldValueDTO
11+
} from '../../../src/datasets/domain/dtos/DatasetDTO'
912
import { SingleMetadataFieldValidator } from '../../../src/datasets/domain/useCases/validators/SingleMetadataFieldValidator'
1013
import { MetadataFieldValidator } from '../../../src/datasets/domain/useCases/validators/MetadataFieldValidator'
1114
import { MultipleMetadataFieldValidator } from '../../../src/datasets/domain/useCases/validators/MultipleMetadataFieldValidator'
@@ -124,7 +127,7 @@ describe('validate', () => {
124127
})
125128

126129
test('should raise an empty field error when a required child field is missing', () => {
127-
const invalidAuthorFieldValue = [
130+
const invalidAuthorFieldValue: DatasetMetadataChildFieldValueDTO[] = [
128131
{ authorName: 'Admin, Dataverse', authorAffiliation: 'Dataverse.org' },
129132
{ authorAffiliation: 'Dataverse.org' }
130133
]
@@ -141,7 +144,7 @@ describe('validate', () => {
141144
})
142145

143146
test('should not raise an empty field error when a not required child field is missing', () => {
144-
const authorFieldValue = [
147+
const authorFieldValue: DatasetMetadataChildFieldValueDTO[] = [
145148
{ authorName: 'Admin, Dataverse', authorAffiliation: 'Dataverse.org' },
146149
{ authorName: 'John, Doe' }
147150
]
@@ -156,15 +159,57 @@ describe('validate', () => {
156159
runValidateExpectingFieldValidationError(
157160
testDataset,
158161
'timePeriodCoveredStart',
159-
'There was an error when validating the field timePeriodCoveredStart from metadata block citation. Reason was: The field requires a valid date format (YYYY-MM-DD).'
162+
'There was an error when validating the field timePeriodCoveredStart from metadata block citation. Reason was: The field requires a valid date format (YYYY or YYYY-MM or YYYY-MM-DD).'
160163
)
161164
})
162165

163-
test('should not raise a date format validation error when a date field has a valid format', () => {
166+
test('should not raise a date format validation error when a date field has a valid YYYY-MM-DD format', () => {
164167
const testDataset = createDatasetDTO(undefined, undefined, undefined, '2020-01-01')
165168
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
166169
})
167170

171+
test('should not raise a date format validation error when a date field has a valid YYYY-MM format', () => {
172+
const testDataset = createDatasetDTO(undefined, undefined, undefined, '2020-01')
173+
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
174+
})
175+
176+
test('should not raise a date format validation error when a date field has a valid YYYY format', () => {
177+
const testDataset = createDatasetDTO(undefined, undefined, undefined, '2020')
178+
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
179+
})
180+
181+
test('should raise a date format validation error when a date field has a wrong date format according to the field watermark', () => {
182+
const testDataset = createDatasetDTO(
183+
undefined,
184+
undefined,
185+
undefined,
186+
undefined,
187+
undefined,
188+
undefined,
189+
'01-03'
190+
)
191+
192+
expect.assertions(6)
193+
runValidateExpectingFieldValidationError(
194+
testDataset,
195+
'dateOfCreation',
196+
'There was an error when validating the field dateOfCreation from metadata block citation. Reason was: The field requires a valid date format (YYYY-MM-DD).'
197+
)
198+
})
199+
200+
test('should not raise a date format validation error when a date field has a valid format according to the field watermark', () => {
201+
const testDataset = createDatasetDTO(
202+
undefined,
203+
undefined,
204+
undefined,
205+
undefined,
206+
undefined,
207+
undefined,
208+
'2024-01-03'
209+
)
210+
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
211+
})
212+
168213
test('should raise a controlled vocabulary error when a controlled vocabulary field has an invalid format', () => {
169214
const testDataset = createDatasetDTO(undefined, undefined, undefined, undefined, 'Wrong Value')
170215

0 commit comments

Comments
 (0)