Skip to content

Commit 73e6a8f

Browse files
committed
fix: allow empty values for conditionally required fields
1 parent 25520a1 commit 73e6a8f

File tree

5 files changed

+132
-12
lines changed

5 files changed

+132
-12
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface DatasetMetadataFieldAndValueInfo {
99
metadataBlockName: string
1010
metadataParentFieldKey?: string
1111
metadataFieldPosition?: number
12+
allowEmptyForConditionallyRequiredField?: boolean
1213
}
1314

1415
export abstract class BaseMetadataFieldValidator {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ export class MetadataFieldValidator extends BaseMetadataFieldValidator {
2323
this.isEmptyString(metadataFieldValue) ||
2424
this.isEmptyArray(metadataFieldValue)
2525
) {
26-
if (metadataFieldInfo.isRequired) {
26+
if (
27+
metadataFieldInfo.isRequired &&
28+
!datasetMetadataFieldAndValueInfo.allowEmptyForConditionallyRequiredField
29+
) {
2730
throw new EmptyFieldError(
2831
datasetMetadataFieldAndValueInfo.metadataFieldKey,
2932
datasetMetadataFieldAndValueInfo.metadataBlockName,

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

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ export class SingleMetadataFieldValidator extends BaseMetadataFieldValidator {
136136
metadataFieldInfo.childMetadataFields as Record<string, MetadataFieldInfo>
137137
)[childMetadataFieldKey]
138138

139+
const allowEmptyForConditionallyRequiredField: boolean =
140+
this.allowEmptyValueForConditionallyRequiredField(
141+
datasetMetadataFieldAndValueInfo,
142+
childMetadataFieldKey
143+
)
144+
139145
metadataFieldValidator.validate({
140146
metadataFieldInfo: childMetadataFieldInfo,
141147
metadataFieldKey: childMetadataFieldKey,
@@ -144,8 +150,58 @@ export class SingleMetadataFieldValidator extends BaseMetadataFieldValidator {
144150
)[childMetadataFieldKey],
145151
metadataBlockName: datasetMetadataFieldAndValueInfo.metadataBlockName,
146152
metadataParentFieldKey: datasetMetadataFieldAndValueInfo.metadataFieldKey,
147-
metadataFieldPosition: datasetMetadataFieldAndValueInfo.metadataFieldPosition
153+
metadataFieldPosition: datasetMetadataFieldAndValueInfo.metadataFieldPosition,
154+
allowEmptyForConditionallyRequiredField
148155
})
149156
}
150157
}
158+
159+
/**
160+
* This method allows setting empty values for conditionally required child fields.
161+
* A child field is conditionally required if it is required and its parent field is not required.
162+
* The childfield should be required only if any of its sibling fields has a value, otherwise it should be optional.
163+
*/
164+
165+
private allowEmptyValueForConditionallyRequiredField(
166+
datasetMetadataFieldAndValueInfo: DatasetMetadataFieldAndValueInfo,
167+
childMetadataFieldKey: string
168+
): boolean {
169+
let result = false
170+
const metadataFieldInfo = datasetMetadataFieldAndValueInfo.metadataFieldInfo
171+
172+
const childMetadataFieldKeys = Object.keys(
173+
metadataFieldInfo.childMetadataFields as Record<string, MetadataFieldInfo>
174+
)
175+
176+
const conditionallyRequiredChildFields: false | string[] =
177+
!datasetMetadataFieldAndValueInfo.metadataFieldInfo.isRequired &&
178+
childMetadataFieldKeys.filter(
179+
(childMetadataFieldKey) =>
180+
(metadataFieldInfo.childMetadataFields as Record<string, MetadataFieldInfo>)[
181+
childMetadataFieldKey
182+
].isRequired
183+
)
184+
const hasConditionallyRequiredChildFields = Boolean(conditionallyRequiredChildFields)
185+
186+
if (
187+
hasConditionallyRequiredChildFields &&
188+
Object.values(conditionallyRequiredChildFields as string[]).includes(childMetadataFieldKey)
189+
) {
190+
// At this point we know we are standing on a child field that is required and the parent field is not required
191+
192+
// Get the sibling fields and check if any of them has a value
193+
const { [childMetadataFieldKey as keyof Record<string, string>]: _, ...siblingFields } =
194+
datasetMetadataFieldAndValueInfo.metadataFieldValue as Record<string, string>
195+
196+
const siblingsValues = Object.values(siblingFields) as string[]
197+
198+
const isAnySiblingValuePresent = siblingsValues.some(
199+
([, value]) => value !== undefined && value !== ''
200+
)
201+
202+
result = !isAnySiblingValuePresent
203+
}
204+
205+
return result
206+
}
151207
}

test/testHelpers/datasets/datasetHelper.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,8 @@ export const createDatasetDTO = (
402402
timePeriodCoveredStartValue?: DatasetMetadataFieldValueDTO,
403403
contributorTypeValue?: DatasetMetadataFieldValueDTO,
404404
license?: DatasetLicense,
405-
dateOfCreationValue?: DatasetMetadataFieldValueDTO
405+
dateOfCreationValue?: DatasetMetadataFieldValueDTO,
406+
contributorNameValue?: string
406407
): DatasetDTO => {
407408
const validTitle = 'test dataset'
408409
const validAuthorFieldValue = [
@@ -434,14 +435,15 @@ export const createDatasetDTO = (
434435
...(dateOfCreationValue && {
435436
dateOfCreation: dateOfCreationValue
436437
}),
437-
...(contributorTypeValue && {
438-
contributor: [
439-
{
440-
contributorName: 'Admin, Dataverse',
441-
contributorType: contributorTypeValue as string
442-
}
443-
]
444-
})
438+
...(contributorTypeValue !== undefined &&
439+
contributorNameValue !== undefined && {
440+
contributor: [
441+
{
442+
contributorName: contributorNameValue ?? 'Admin, Dataverse',
443+
contributorType: (contributorTypeValue as string | undefined) ?? 'Project Member'
444+
}
445+
]
446+
})
445447
}
446448
}
447449
]

test/unit/datasets/DatasetResourceValidator.test.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,16 @@ describe('validate', () => {
213213
})
214214

215215
test('should raise a controlled vocabulary error when a controlled vocabulary field has an invalid format', () => {
216-
const testDataset = createDatasetDTO(undefined, undefined, undefined, undefined, 'Wrong Value')
216+
const testDataset = createDatasetDTO(
217+
undefined,
218+
undefined,
219+
undefined,
220+
undefined,
221+
'Wrong Value',
222+
undefined,
223+
undefined,
224+
'Some Value'
225+
)
217226

218227
expect.assertions(6)
219228
runValidateExpectingFieldValidationError(
@@ -235,4 +244,53 @@ describe('validate', () => {
235244
)
236245
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
237246
})
247+
248+
test("should not raise an error when there is a compound field not required with a required childfield that has no value but siblings don't have value either", () => {
249+
const testDataset = createDatasetDTO(
250+
undefined,
251+
undefined,
252+
undefined,
253+
undefined,
254+
'',
255+
undefined,
256+
undefined,
257+
''
258+
)
259+
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
260+
})
261+
262+
test('should not raise an error when there is a compound field not required with a required childfield that has value and some of the siblings has value', () => {
263+
const testDataset = createDatasetDTO(
264+
undefined,
265+
undefined,
266+
undefined,
267+
undefined,
268+
'Project Member',
269+
undefined,
270+
undefined,
271+
'Foo'
272+
)
273+
expect(() => sut.validate(testDataset, testMetadataBlocks)).not.toThrow()
274+
})
275+
276+
test('should raise an error when there is a compound field not required with a required childfield that has no value but some of the siblings have', () => {
277+
const testDataset = createDatasetDTO(
278+
undefined,
279+
undefined,
280+
undefined,
281+
undefined,
282+
'Project Member',
283+
undefined,
284+
undefined,
285+
''
286+
)
287+
expect.assertions(6)
288+
runValidateExpectingFieldValidationError(
289+
testDataset,
290+
'contributorName',
291+
'There was an error when validating the field contributorName from metadata block citation with parent field contributor in position 0. Reason was: The field should not be empty.',
292+
'contributor',
293+
0
294+
)
295+
})
238296
})

0 commit comments

Comments
 (0)