@@ -359,6 +359,9 @@ const hasBloatedLineage = ( data: Record<string, unknown> ): boolean => {
359
359
// our liking as `isNormalized` check ensures that it does have two keys `_id` and `parent`.
360
360
return true ;
361
361
}
362
+ if ( ! parent . parent ) {
363
+ return Object . keys ( parent ) . length > 1 ;
364
+ }
362
365
parent = parent . parent ;
363
366
}
364
367
return false ;
@@ -373,3 +376,69 @@ const hasValidContactType = ( data: Record<string, unknown> ): boolean => {
373
376
const hasValidLegacyContactType = ( data : Record < string , unknown > , type :string ) : boolean => {
374
377
return data . type === type ;
375
378
} ;
379
+
380
+ /**
381
+ * A qualifier for a place
382
+ */
383
+ export type PlaceQualifier = ContactQualifier & Readonly < {
384
+ parent ?: NormalizedParent ;
385
+ contact ?: NormalizedParent ;
386
+ place_id ?: string ;
387
+ } >
388
+
389
+ /**
390
+ * Builds a qualifier for creation and update of a place with the given fields
391
+ * @param data object containing the fields for a person
392
+ * @returns the place qualifier
393
+ * @throws Error if data is not an object
394
+ * @throws Error if type is not provided or is empty
395
+ * @throws Error if name is not provided or is empty
396
+ * @throws Error if parent if present is not in a valid de-hydrated format.
397
+ * @throws Error if contact if present is not in a valid de-hydrated format.
398
+ * @throws Error if reported_date is not in a valid format.
399
+ * Valid formats are 'YYYY-MM-DDTHH:mm:ssZ', 'YYYY-MM-DDTHH:mm:ss.SSSZ', or <unix epoch>.
400
+ */
401
+ export const byPlaceQualifier = ( data :unknown ) : PlaceQualifier => {
402
+ const qualifier = byContactQualifierNonAssertive ( data ) ;
403
+ if ( 'parent' in qualifier ) {
404
+ if ( ! isNormalizedParent ( qualifier . parent ) ) {
405
+ throw new InvalidArgumentError ( `Missing required fields in the parent hierarchy [${ JSON . stringify ( qualifier ) } ].` ) ;
406
+ }
407
+ if ( hasBloatedLineage ( qualifier ) ) {
408
+ throw new InvalidArgumentError ( `Additional fields found in the parent lineage [${ JSON . stringify ( qualifier ) } ].` ) ;
409
+ }
410
+ }
411
+
412
+ if ( hasField ( qualifier , { name : 'contact' , type : 'object' } ) ) {
413
+ if ( ! isNormalizedParent ( qualifier . contact ) ) {
414
+ // eslint-disable-next-line max-len
415
+ throw new InvalidArgumentError ( `Missing required fields in the contact hierarchy [${ JSON . stringify ( qualifier ) } ].` ) ;
416
+ }
417
+ if ( hasBloatedLineage ( qualifier . contact ) ) {
418
+ throw new InvalidArgumentError ( `Additional fields found in the contact lineage [${ JSON . stringify ( qualifier ) } ].` ) ;
419
+ }
420
+ }
421
+
422
+ if ( ! hasValidContactType ( qualifier ) && ! hasValidLegacyContactType ( qualifier , 'place' ) ) {
423
+ throw new InvalidArgumentError ( 'Invalid type for contacts.' ) ;
424
+ }
425
+
426
+ return qualifier as PlaceQualifier ;
427
+ } ;
428
+
429
+ /** @internal */
430
+ export const isPlaceQualifier = ( data :unknown ) : data is PlaceQualifier => {
431
+ if ( ! checkContactQualifierFields ( data ) ) {
432
+ return false ;
433
+ }
434
+
435
+ if ( hasField ( data , { name : 'parent' , type : 'object' } ) ) {
436
+ return isNormalizedParent ( data . parent ) && ! hasBloatedLineage ( data ) ;
437
+ }
438
+
439
+ if ( hasField ( data , { name : 'contact' , type : 'object' } ) ) {
440
+ return isNormalizedParent ( data . contact ) && ! hasBloatedLineage ( data . contact ) ;
441
+ }
442
+
443
+ return hasValidContactType ( data ) || hasValidLegacyContactType ( data , 'place' ) ;
444
+ } ;
0 commit comments