diff --git a/app/common/angular-utility.ts b/app/common/angular-utility.ts index 7d51b83ac3..dc8bc6c37c 100644 --- a/app/common/angular-utility.ts +++ b/app/common/angular-utility.ts @@ -3,8 +3,8 @@ */ export module AngularUtility { - export async function refresh() { + export async function refresh(duration: number = 1) { - await new Promise(resolve => setTimeout(async () => resolve(), 1)); + await new Promise(resolve => setTimeout(async () => resolve(), duration)); } } \ No newline at end of file diff --git a/app/components/import/import.component.ts b/app/components/import/import.component.ts index 00dcc30900..fcca190e21 100644 --- a/app/components/import/import.component.ts +++ b/app/components/import/import.component.ts @@ -28,6 +28,7 @@ import BASE_EXCLUSION = ExportRunner.BASE_EXCLUSION; import getTypesWithoutExcludedTypes = ExportRunner.getTypesWithoutExcludedTypes; import {IdaiType} from '../../core/configuration/model/idai-type'; import {ProjectConfiguration} from '../../core/configuration/project-configuration'; +import {AngularUtility} from '../../common/angular-utility'; @Component({ @@ -57,6 +58,7 @@ export class ImportComponent implements OnInit { public mergeMode = false; public permitDeletions = false; public javaInstalled: boolean = true; + public running: boolean = false; // CSV Import public resourceTypes: Array = []; @@ -116,38 +118,26 @@ export class ImportComponent implements OnInit { } - public async startImport() { + public async onImportButtonClick() { - this.messages.removeAllMessages(); - - const reader: Reader|undefined = ImportComponent.createReader(this.sourceType, this.format, - this.file as any, this.url as any, this.http); - if (!reader) return this.messages.add([M.IMPORT_READER_GENERIC_START_ERROR]); - - let uploadModalRef: any = undefined; - let uploadReady = false; - setTimeout(() => { - if (!uploadReady) uploadModalRef = this.modalService.open(UploadModalComponent, - { backdrop: 'static', keyboard: false }); - }, 200); - - this.settingsService.stopSync(); + if (!this.isReady()) return; - const importReport = await this.doImport(reader); - - this.settingsService.startSync(); + this.running = true; + await AngularUtility.refresh(100); + await this.startImport(); - uploadReady = true; - if(uploadModalRef) uploadModalRef.close(); - this.showImportResult(importReport); + // The timeout is necessary to prevent another import from starting if the import button is clicked + // again while the import is running + setTimeout(() => this.running = false, 100); } public isReady(): boolean|undefined { - return this.sourceType === 'file' - ? this.file !== undefined - : this.url !== undefined; + return !this.running + && this.sourceType === 'file' + ? this.file !== undefined + : this.url !== undefined; } @@ -195,6 +185,31 @@ export class ImportComponent implements OnInit { } + private async startImport() { + + this.messages.removeAllMessages(); + + const reader: Reader|undefined = ImportComponent.createReader(this.sourceType, this.format, + this.file as any, this.url as any, this.http); + if (!reader) return this.messages.add([M.IMPORT_READER_GENERIC_START_ERROR]); + + let uploadModalRef: any = undefined; + let uploadReady = false; + setTimeout(() => { + if (!uploadReady) uploadModalRef = this.modalService.open(UploadModalComponent, + { backdrop: 'static', keyboard: false }); + }, 200); + + this.settingsService.stopSync(); + const importReport = await this.doImport(reader); + this.settingsService.startSync(); + + uploadReady = true; + if(uploadModalRef) uploadModalRef.close(); + this.showImportResult(importReport); + } + + private updateResourceTypes() { this.resourceTypes = getTypesWithoutExcludedTypes( diff --git a/app/components/import/import.html b/app/components/import/import.html index 4305caf6a4..5d3023524b 100644 --- a/app/components/import/import.html +++ b/app/components/import/import.html @@ -5,7 +5,7 @@ diff --git a/app/components/import/import.scss b/app/components/import/import.scss index 6e0c447bc1..d881f6204e 100644 --- a/app/components/import/import.scss +++ b/app/components/import/import.scss @@ -4,14 +4,8 @@ */ .import { - .spinner { - position: relative; - top: 15px; - left: 5px; - } - .disabled { - cursor: not-allowed; + transition: none !important; } .import-info { diff --git a/app/core/datastore/core/solve-project-document-conflicts.ts b/app/core/datastore/core/solve-project-document-conflicts.ts index ba57cd9b69..f5d422f529 100644 --- a/app/core/datastore/core/solve-project-document-conflicts.ts +++ b/app/core/datastore/core/solve-project-document-conflicts.ts @@ -1,4 +1,4 @@ -import {assoc, to, lookup, flow, map, filter, isDefined, union as tsfunUnion, equal, +import {assoc, assocOn, to, lookup, flow, map, filter, isDefined, union as tsfunUnion, equal, isEmpty, compose, dissoc, append} from 'tsfun'; import {Document, Resource} from 'idai-components-2'; import {DatastoreUtil} from './datastore-util'; @@ -41,7 +41,7 @@ export function solveProjectDocumentConflict(latestRevision: Document, if (resource[CAMPAIGNS] && resource[CAMPAIGNS].length === 0) delete resource[CAMPAIGNS]; // this is to work with the latest changes history - const latestRevisionDocumentWithInsertedResultResource = assoc(RESOURCE, resource)(clonedLatestRevision); + const latestRevisionDocumentWithInsertedResultResource = assocOn(RESOURCE, resource)(clonedLatestRevision); return [latestRevisionDocumentWithInsertedResultResource, revisionIds]; } diff --git a/app/core/datastore/field/field-type-converter.ts b/app/core/datastore/field/field-type-converter.ts index 08134f5f3e..e2bcffd962 100644 --- a/app/core/datastore/field/field-type-converter.ts +++ b/app/core/datastore/field/field-type-converter.ts @@ -51,15 +51,8 @@ export class FieldTypeConverter extends TypeConverter { } else { takeOrMake(document, 'resource.identifier',''); takeOrMake(document, 'resource.relations.isRecordedIn', []); - - if (this.typeUtility.isSubtype(document.resource.type,'Feature')) { - takeOrMake(document, 'resource.relations.isContemporaryWith', []); - takeOrMake(document, 'resource.relations.isAfter', []); - takeOrMake(document, 'resource.relations.isBefore', []); - } } - return Migrator.migrate(document) as T; } } \ No newline at end of file diff --git a/app/core/datastore/index/constraint-index.ts b/app/core/datastore/index/constraint-index.ts index 3962433fbc..387178e910 100644 --- a/app/core/datastore/index/constraint-index.ts +++ b/app/core/datastore/index/constraint-index.ts @@ -1,4 +1,4 @@ -import {getOnOr} from 'tsfun'; +import {getOn} from 'tsfun'; import {Document} from 'idai-components-2'; import {IndexItem} from './index-item'; import {IdaiType} from '../../configuration/model/idai-type'; @@ -142,7 +142,7 @@ export module ConstraintIndex { function putFor(constraintIndex: ConstraintIndex, indexDefinition: IndexDefinition, doc: Document) { - const elForPath = getOnOr(indexDefinition.path, undefined)(doc); + const elForPath = getOn(indexDefinition.path, undefined)(doc); switch(indexDefinition.type) { case 'exist': diff --git a/app/core/import/exec/default-import.ts b/app/core/import/exec/default-import.ts index 73b315183e..29894e4274 100644 --- a/app/core/import/exec/default-import.ts +++ b/app/core/import/exec/default-import.ts @@ -62,7 +62,7 @@ export function buildImportFunction(validator: ImportValidator, return { errors: [errWithParams], successfulImports: 0 }; } - const [ importDocuments, targetDocuments, msgWithParams ] = await process( + const [importDocuments, targetDocuments, msgWithParams] = await process( documents, validator, operationTypeNames, @@ -80,14 +80,12 @@ export function buildImportFunction(validator: ImportValidator, const updateErrors = []; try { - await Updater.go( documentsForImport, targetDocuments, datastore, username, importOptions.mergeMode === true); - } catch (errWithParams) { updateErrors.push(errWithParams) } diff --git a/app/core/import/exec/preprocess-fields.ts b/app/core/import/exec/preprocess-fields.ts index 209f49ada1..4cdf628f13 100644 --- a/app/core/import/exec/preprocess-fields.ts +++ b/app/core/import/exec/preprocess-fields.ts @@ -28,15 +28,12 @@ function collapseEmptyProperties(struct: any|undefined, permitDeletions: boolean keys(struct) .map(pairWith(lookup(struct))) .forEach(([fieldName, fieldValue]: any) => { + if (fieldName === 'relations') return; if (fieldValue === null) { - if (!permitDeletions) delete struct[fieldName]; - } else if (typeof (fieldValue as any) === 'string' && fieldValue === '') { - throw [ImportErrors.MUST_NOT_BE_EMPTY_STRING]; - } else if (isObject(fieldValue) || isArray(fieldValue)) { collapseEmptyProperties(fieldValue, permitDeletions); diff --git a/app/core/import/exec/preprocess-relations.ts b/app/core/import/exec/preprocess-relations.ts index 93d85908ec..c1643c7be9 100644 --- a/app/core/import/exec/preprocess-relations.ts +++ b/app/core/import/exec/preprocess-relations.ts @@ -1,9 +1,8 @@ -import {Document} from 'idai-components-2/src/model/core/document'; +import {hasNot, includedIn, isArray, isnt, isUndefinedOrEmpty} from 'tsfun'; +import {Document, Relations} from 'idai-components-2'; import {Find, Get, Id, Identifier, IdentifierMap} from './types'; -import {Relations} from 'idai-components-2/src/model/core/relations'; import {iterateRelationsInImport} from './utils'; import {ImportErrors as E} from './import-errors'; -import {hasNot, includedIn, isArray, isnt, isUndefinedOrEmpty} from 'tsfun'; import {RESOURCE_ID} from '../../../c'; import {HIERARCHICAL_RELATIONS, PARENT} from '../../model/relation-constants'; import LIES_WITHIN = HIERARCHICAL_RELATIONS.LIES_WITHIN; @@ -28,7 +27,7 @@ export async function preprocessRelations(documents: Array, generateId: () => string, find: Find, get: Get, - { mergeMode, useIdentifiersInRelations}: ImportOptions) { + { mergeMode, permitDeletions, useIdentifiersInRelations}: ImportOptions) { const identifierMap: IdentifierMap = mergeMode ? {} : assignIds(documents, generateId); @@ -40,6 +39,7 @@ export async function preprocessRelations(documents: Array, } adjustRelations(document, relations); removeSelfReferencingIdentifiers(relations, document.resource.identifier); + if (!permitDeletions) removeEmptyRelations(relations); if (useIdentifiersInRelations) { await rewriteIdentifiersInRelations(relations, find, identifierMap); } else { @@ -139,4 +139,14 @@ function removeSelfReferencingIdentifiers(relations: Relations, resourceIdentifi relations[relName] = relations[relName].filter(isnt(resourceIdentifier)); if (isUndefinedOrEmpty(relations[relName])) delete relations[relName]; } +} + + +function removeEmptyRelations(relations: Relations) { + + for (let relName of Object.keys(relations)) { + if (relations[relName] === null || relations[relName] === []) { + delete relations[relName]; + } + } } \ No newline at end of file diff --git a/app/core/import/exec/process/complete-inverse-relations.ts b/app/core/import/exec/process/complete-inverse-relations.ts index 95accbbb57..7cf9def9d5 100644 --- a/app/core/import/exec/process/complete-inverse-relations.ts +++ b/app/core/import/exec/process/complete-inverse-relations.ts @@ -1,8 +1,8 @@ -import {Document, Relations} from 'idai-components-2'; -import {ImportErrors as E} from '../import-errors'; import {compose, filter, flatten, flow, forEach, intersect, isDefined, - isEmpty, isNot, isUndefinedOrEmpty, lookup, keys, values, empty, + isNot, isUndefinedOrEmpty, lookup, keys, values, empty, map, remove, subtract, to, undefinedOrEmpty} from 'tsfun'; +import {Document, Relations} from 'idai-components-2'; +import {ImportErrors as E} from '../import-errors'; import {HIERARCHICAL_RELATIONS, POSITION_RELATIONS, TIME_RELATIONS} from '../../../model/relation-constants'; import {setInverseRelationsForDbResources} from './set-inverse-relations-for-db-resources'; import {assertInSameOperationWith, makeDocumentsLookup} from '../utils'; @@ -56,7 +56,7 @@ export async function completeInverseRelations(importDocuments: Array, const lookupDocument = lookup(makeDocumentsLookup(importDocuments)); - setInverseRelationsForImportResource( + setInverseRelationsForImportResources( importDocuments, lookupDocument, pairRelationWithItsInverse(getInverseRelation), @@ -80,13 +80,21 @@ function getTargetIds(mergeMode: boolean, let targetIds = targetIdsReferingToDbResources(document, lookupDocument); if (mergeMode) { - let oldVersion; try { oldVersion = await get(document.resource.id); - } catch { throw "FATAL existing version of document not found" } + let oldVersion; + try { + oldVersion = await get(document.resource.id); + } catch { + throw 'FATAL: Existing version of document not found'; + } + return [ targetIds, - subtract(targetIds)(targetIdsReferingToDbResources(oldVersion as any, lookupDocument))]; + subtract(targetIds)( + targetIdsReferingToDbResources(oldVersion as any, lookupDocument) + ) + ]; } - return [ targetIds, [] ]; + return [targetIds, []]; } } @@ -102,21 +110,21 @@ function targetIdsReferingToDbResources(document: Document, } -function setInverseRelationsForImportResource(importDocuments: Array, - lookupDocument: LookupDocument, - pairRelationWithItsInverse: PairRelationWithItsInverse, - assertIsAllowedRelationDomainType: AssertIsAllowedRelationDomainType): void { +function setInverseRelationsForImportResources(importDocuments: Array, + lookupDocument: LookupDocument, + pairRelationWithItsInverse: PairRelationWithItsInverse, + assertIsAllowedRelationDomainType: AssertIsAllowedRelationDomainType): void { for (let importDocument of importDocuments) { const pairRelationWithItsInverse_ = pairRelationWithItsInverse(importDocument); - const assertNotBadyInterrelated_ = assertNotBadlyInterrelated(importDocument); + const assertNotBadlyInterrelated_ = assertNotBadlyInterrelated(importDocument); const setInverses_ = setInverses(importDocument, lookupDocument, assertIsAllowedRelationDomainType); flow(importDocument.resource.relations, keys, map(pairRelationWithItsInverse_), - forEach(assertNotBadyInterrelated_), + forEach(assertNotBadlyInterrelated_), forEach(setInverses_)); } } @@ -156,18 +164,17 @@ function setInverses(importDocument: Document, function pairRelationWithItsInverse(getInverseRelation: (_: string) => string|undefined) { - return (document: Document) => (relationName: string): [string, string|undefined] => { + return (document: Document) => (relationName: string): [string, string|undefined] => { - if (relationName === RECORDED_IN) return [RECORDED_IN, undefined]; - if (isEmpty(document.resource.relations[relationName])) { - throw [E.EMPTY_RELATION, document.resource.identifier]; + if (relationName === RECORDED_IN) { + return [RECORDED_IN, undefined]; + } else { + return [relationName, getInverseRelation(relationName)]; } - return [relationName, getInverseRelation(relationName)]; } } - function assertNotBadlyInterrelated(document: Document) { return ([relationName, inverseRelationName]: [string, string|undefined]) => { @@ -176,20 +183,21 @@ function assertNotBadlyInterrelated(document: Document) { const forbiddenRelations = []; - if (relationName !== inverseRelationName) forbiddenRelations.push(inverseRelationName); + if (relationName !== inverseRelationName) forbiddenRelations.push(inverseRelationName); if ([IS_ABOVE, IS_BELOW].includes(relationName)) forbiddenRelations.push(IS_EQUIVALENT_TO); - else if (IS_EQUIVALENT_TO === relationName) forbiddenRelations.push(IS_ABOVE, IS_BELOW); + else if (IS_EQUIVALENT_TO === relationName) forbiddenRelations.push(IS_ABOVE, IS_BELOW); if ([IS_BEFORE, IS_AFTER].includes(relationName)) forbiddenRelations.push(IS_CONTEMPORARY_WITH); - else if (IS_CONTEMPORARY_WITH === relationName) forbiddenRelations.push(IS_BEFORE, IS_AFTER); + else if (IS_CONTEMPORARY_WITH === relationName) forbiddenRelations.push(IS_BEFORE, IS_AFTER); assertNoForbiddenRelations(forbiddenRelations, document.resource.relations[relationName], document); } } -function assertNoForbiddenRelations(forbiddenRelations: string[], relationTargets: string[], document: Document) { +function assertNoForbiddenRelations(forbiddenRelations: string[], relationTargets: string[], + document: Document) { forbiddenRelations .map(lookup(document.resource.relations)) diff --git a/app/core/import/exec/process/merge-document.ts b/app/core/import/exec/process/merge-document.ts index 52a1aee8e7..c7801a81a9 100644 --- a/app/core/import/exec/process/merge-document.ts +++ b/app/core/import/exec/process/merge-document.ts @@ -40,9 +40,11 @@ function overwriteOrDeleteProperties(target: any|undefined, source: any, exclusi return Object.keys(source) .filter(isNot(includedIn(exclusions))) .reduce((target: {[propertyName: string]: string[]}, propertyName: string) => { - - if (source[propertyName] === null) delete target[propertyName]; - else target[propertyName] = source[propertyName]; + if (source[propertyName] === null) { + delete target[propertyName]; + } else { + target[propertyName] = source[propertyName]; + } return target; }, target ? target : {}); diff --git a/app/core/import/exec/process/process-relations.ts b/app/core/import/exec/process/process-relations.ts index 7f4857ddc6..7829f1f1bf 100644 --- a/app/core/import/exec/process/process-relations.ts +++ b/app/core/import/exec/process/process-relations.ts @@ -1,12 +1,11 @@ -import {Document} from 'idai-components-2/src/model/core/document'; +import {and, Either, empty, isDefined, isNot, isUndefinedOrEmpty, on, sameset, to, + undefinedOrEmpty} from 'tsfun'; +import {Document, NewDocument, Relations} from 'idai-components-2'; import {ImportValidator} from './import-validator'; -import {NewDocument} from 'idai-components-2/src/model/core/new-document'; -import {and, Either, empty, isDefined, isNot, isUndefinedOrEmpty, on, sameset, to, undefinedOrEmpty} from 'tsfun'; import {ImportErrors as E} from '../import-errors'; import {HIERARCHICAL_RELATIONS} from '../../../model/relation-constants'; import LIES_WITHIN = HIERARCHICAL_RELATIONS.LIES_WITHIN; import RECORDED_IN = HIERARCHICAL_RELATIONS.RECORDED_IN; -import {Relations} from 'idai-components-2'; import {Get, GetInverseRelation, Id, IdMap} from '../types'; import {completeInverseRelations} from './complete-inverse-relations'; import {ImportOptions} from '../default-import'; @@ -21,8 +20,7 @@ export async function processRelations(documents: Array, operationTypeNames: string[], getInverseRelation: GetInverseRelation, get: Get, - { mergeMode, permitDeletions, - mainTypeDocumentId} : ImportOptions) { + { mergeMode, permitDeletions, mainTypeDocumentId }: ImportOptions) { const assertIsAllowedRelationDomainType_ = (_: any, __: any, ___: any, ____: any) => validator.assertIsAllowedRelationDomainType(_, __, ___, ____); @@ -35,18 +33,14 @@ export async function processRelations(documents: Array, await replaceTopLevelLiesWithins(documents, operationTypeNames, get, mainTypeDocumentId ? mainTypeDocumentId : ''); await inferRecordedIns(documents, operationTypeNames, get, makeAssertNoRecordedInMismatch(mainTypeDocumentId ? mainTypeDocumentId : '')); - if (!mergeMode || permitDeletions) { - - await validator.assertRelationsWellformedness(documents); - await validator.assertLiesWithinCorrectness(documents.map(to('resource'))); - return await completeInverseRelations( - documents, - get, - getInverseRelation, - assertIsAllowedRelationDomainType_, - mergeMode); - } - return []; + await validator.assertRelationsWellformedness(documents); + await validator.assertLiesWithinCorrectness(documents.map(to('resource'))); + return await completeInverseRelations( + documents, + get, + getInverseRelation, + assertIsAllowedRelationDomainType_, + mergeMode); } @@ -109,7 +103,7 @@ async function inferRecordedIns(documents: Array, return operationTypeNames.includes(got.resource.type) ? got.resource.id : got.resource.relations[RECORDED_IN][0]; - } catch { console.log("FATAL - not found") } // should have been caught earlier, in process() + } catch { console.log('FATAL: Not found'); } // should have been caught earlier, in process() } @@ -190,8 +184,8 @@ async function replaceTopLevelLiesWithins(documents: Array, function searchInImport(targetDocumentResourceId: Id, idMap: IdMap, operationTypeNames: string[] -): Either // recordedInResourceId|targetDocument - |undefined { // targetDocument not found + ): Either // recordedInResourceId|targetDocument + |undefined { // targetDocument not found const targetInImport = idMap[targetDocumentResourceId]; if (!targetInImport) return undefined; diff --git a/app/core/import/exec/process/process.ts b/app/core/import/exec/process/process.ts index 7eb1bae601..8f85406379 100644 --- a/app/core/import/exec/process/process.ts +++ b/app/core/import/exec/process/process.ts @@ -1,7 +1,7 @@ +import {duplicates, to, dissocOn} from 'tsfun'; +import {Document, NewDocument} from 'idai-components-2'; import {ImportValidator} from './import-validator'; -import {duplicates, to, dissoc} from 'tsfun'; import {ImportErrors as E} from '../import-errors'; -import {Document, NewDocument} from 'idai-components-2'; import {RESOURCE_IDENTIFIER} from '../../../../c'; import {processRelations} from './process-relations'; import {Get, GetInverseRelation} from '../types'; @@ -73,7 +73,8 @@ export async function process(documents: Array, operationTypeNames, getInverseRelation, get, - importOptions); + importOptions + ); return [processedDocuments, relatedDocuments, undefined]; @@ -110,7 +111,7 @@ function processDocuments(documents: Array, validator: ImportValidator function mergeOrUseAsIs(document: NewDocument|Document): Document { return (document as any)[MERGE_TARGET] - ? mergeDocument((document as any)[MERGE_TARGET], dissoc(MERGE_TARGET)(document)) + ? mergeDocument((document as any)[MERGE_TARGET], dissocOn(MERGE_TARGET)(document)) : document as Document; } diff --git a/app/core/import/exec/process/set-inverse-relations-for-db-resources.ts b/app/core/import/exec/process/set-inverse-relations-for-db-resources.ts index cdae19c996..115531e78a 100644 --- a/app/core/import/exec/process/set-inverse-relations-for-db-resources.ts +++ b/app/core/import/exec/process/set-inverse-relations-for-db-resources.ts @@ -1,7 +1,7 @@ -import {Document} from 'idai-components-2'; -import {ImportErrors as E} from '../import-errors'; import {is, on, union, isNot, includedIn, keysAndValues} from 'tsfun'; import {asyncMap, asyncReduce} from 'tsfun-extra'; +import {Document} from 'idai-components-2'; +import {ImportErrors as E} from '../import-errors'; import {ConnectedDocsResolution} from '../../../model/connected-docs-resolution'; import {clone} from '../../../util/object-util'; import {ResourceId} from '../../../../c'; @@ -13,8 +13,8 @@ import {AssertIsAllowedRelationDomainType} from '../types'; * @param importDocuments * @param getTargetIds a pair of id lists, where the first list's ids * are of resources already in the db and referenced by the current version of the importDocument, - * and the second lists'ids are resources already in the db and referenced - * by the version to be updated of importDocument, where only ids which are not in the first list, are listed. + * and the second list's ids are resources already in the db and referenced by the version + * to be updated of importDocument, where only ids that are not in the first list are listed. * @param get * @param getInverseRelation * @param assertIsAllowedRelationDomainType @@ -24,12 +24,12 @@ import {AssertIsAllowedRelationDomainType} from '../types'; * @author Thomas Kleinke */ export async function setInverseRelationsForDbResources( - importDocuments: Array, - getTargetIds: (document: Document) => Promise<[ResourceId[], ResourceId[]]>, - get: (_: string) => Promise, - getInverseRelation: (_: string) => string|undefined, - assertIsAllowedRelationDomainType: AssertIsAllowedRelationDomainType, - unidirectionalRelations: string[]): Promise> { + importDocuments: Array, + getTargetIds: (document: Document) => Promise<[ResourceId[], ResourceId[]]>, + get: (_: string) => Promise, + getInverseRelation: (_: string) => string|undefined, + assertIsAllowedRelationDomainType: AssertIsAllowedRelationDomainType, + unidirectionalRelations: string[]): Promise> { let allFetchedDocuments: Array = []; // store already fetched documents @@ -56,7 +56,8 @@ export async function setInverseRelationsForDbResources( } -function reduceToDBDocumentsToBeUpdated(getDocumentTargetDocsToUpdate: (document: Document) => Promise>) { +function reduceToDBDocumentsToBeUpdated( + getDocumentTargetDocsToUpdate: (document: Document) => Promise>) { return asyncReduce( async (totalDocsToUpdate: Array, document: Document) => { @@ -74,10 +75,8 @@ function reduceToDBDocumentsToBeUpdated(getDocumentTargetDocsToUpdate: (document * and the document here does not reference a targetDocument with a bi-directional relation, * there will be no update for that targetDocument */ -function getRidOfUnnecessaryTargetDocs( - document: Document, - targetDocuments: Array, - unidirectionalRelations: string[]) { +function getRidOfUnnecessaryTargetDocs(document: Document, targetDocuments: Array, + unidirectionalRelations: string[]) { return targetDocuments.filter(targetDocument => { for (let k of Object @@ -115,7 +114,9 @@ function assertTypeIsInRange(document: Document, for (let relationTarget of relationTargets) { const targetType = idTypeMap[relationTarget]; if (!targetType) continue; - assertIsAllowedRelationDomainType(document.resource.type, targetType, relationName, document.resource.identifier); + assertIsAllowedRelationDomainType( + document.resource.type, targetType, relationName, document.resource.identifier + ); } }) } @@ -130,7 +131,7 @@ function getTargetDocument(documents: Array, get: Function) { if (!targetDocument) try { targetDocument = clone(await get(targetId)); } catch { - throw [E.EXEC_MISSING_RELATION_TARGET, targetId] + throw [E.EXEC_MISSING_RELATION_TARGET, targetId]; } return targetDocument as Document; } diff --git a/app/core/import/exec/utils.ts b/app/core/import/exec/utils.ts index ee88606561..1b8813f0ea 100644 --- a/app/core/import/exec/utils.ts +++ b/app/core/import/exec/utils.ts @@ -1,6 +1,6 @@ import {Document} from 'idai-components-2/src/model/core/document'; import {unionBy} from 'tsfun-core'; -import {arrayEqual, getOnOr, isNot, on, undefinedOrEmpty} from 'tsfun'; +import {arrayEqual, getOn, isNot, on, undefinedOrEmpty} from 'tsfun'; import {ImportErrors as E} from './import-errors'; import {HIERARCHICAL_RELATIONS} from '../../model/relation-constants'; import RECORDED_IN = HIERARCHICAL_RELATIONS.RECORDED_IN; @@ -27,8 +27,8 @@ export function assertLegalCombination(mergeMode: boolean|undefined, mainTypeDoc export function assertInSameOperationWith(document: Document) { return (targetDocument: Document) => { - const documentRecordedIn = getOnOr('resource.relations.' + RECORDED_IN, undefined)(document); - const targetDocumentRecordedIn = getOnOr('resource.relations.' + RECORDED_IN, undefined)(targetDocument); + const documentRecordedIn = getOn('resource.relations.' + RECORDED_IN, undefined)(document); + const targetDocumentRecordedIn = getOn('resource.relations.' + RECORDED_IN, undefined)(targetDocument); if (isNot(undefinedOrEmpty)(documentRecordedIn) diff --git a/app/core/import/importer.ts b/app/core/import/importer.ts index 42c3610544..c53858b2d6 100644 --- a/app/core/import/importer.ts +++ b/app/core/import/importer.ts @@ -154,7 +154,7 @@ export module Importer { operationTypeNames: string[], mainTypeDocumentId: string, mergeMode: boolean, - updateRelationsOnMergeMode: boolean, + permitDeletions: boolean, getInverseRelation: (_: string) => string|undefined, generateId: () => string, postProcessDocument: (document: Document) => Document, @@ -167,13 +167,15 @@ export module Importer { case 'geojson-gazetteer': importFunction = buildImportFunction(validator, operationTypeNames, getInverseRelation, generateId, postProcessDocument, { mergeMode: false, permitDeletions: false}); + break; case 'shapefile': case 'geojson': importFunction = buildImportFunction(validator, operationTypeNames, getInverseRelation, generateId, postProcessDocument, { mergeMode: true, permitDeletions: false}); + break; default: // native | csv importFunction = buildImportFunction(validator, operationTypeNames, getInverseRelation, generateId, postProcessDocument, - { mergeMode: mergeMode, permitDeletions: updateRelationsOnMergeMode, + { mergeMode: mergeMode, permitDeletions: permitDeletions, mainTypeDocumentId: mainTypeDocumentId, useIdentifiersInRelations: true}); } diff --git a/app/core/import/parser/csv-field-types-conversion.ts b/app/core/import/parser/csv-field-types-conversion.ts index 6a08265b7a..939679d9a4 100644 --- a/app/core/import/parser/csv-field-types-conversion.ts +++ b/app/core/import/parser/csv-field-types-conversion.ts @@ -1,4 +1,5 @@ -import {getOnOr, includedIn, is, isNot, on, setOn, isnt} from 'tsfun'; +import {getOn, includedIn, is, isNot, on, isnt} from 'tsfun'; +import {setOn} from 'tsfun-extra'; import {Resource, Dimension, Dating} from 'idai-components-2'; import {ParserErrors} from './parser-errors'; import {CSVExport} from '../../export/csv-export'; @@ -122,7 +123,7 @@ export module CsvFieldTypesConversion { */ function convertNumber(container: any, path: string, type: 'int'|'float') { - const val = getOnOr(path, undefined)(container); + const val = getOn(path, undefined)(container); if (!val) return; const converted = type === 'int' ? parseInt(val) : parseFloat(val); if (isNaN(converted)) throw [ParserErrors.CSV_NOT_A_NUMBER, val, path]; @@ -136,7 +137,7 @@ export module CsvFieldTypesConversion { */ function convertBoolean(container: any, path: string) { - const val = getOnOr(path, undefined)(container); + const val = getOn(path, undefined)(container); if (!val) return; if (isNot(includedIn(['true', 'false']))(val)) throw [ParserErrors.CSV_NOT_A_BOOLEAN, val, path]; setOn(container, path)(val === 'true'); diff --git a/app/core/import/parser/csv-rows-conversion.ts b/app/core/import/parser/csv-rows-conversion.ts index d95dbf9be9..7018bbf491 100644 --- a/app/core/import/parser/csv-rows-conversion.ts +++ b/app/core/import/parser/csv-rows-conversion.ts @@ -35,7 +35,7 @@ export module CsvRowsConversion { const convertIfEmpty = (val: string) => val === '' ? null : val; - function insertFieldIntoDocument(struct: any, fieldOfHeading: any, fieldOfRow: any) { + function insertFieldIntoDocument(struct: any, fieldOfHeading: string, fieldOfRow: any) { if (fieldOfHeading.includes(PATH_SEPARATOR)) { implodePaths(struct, fieldOfHeading.split(PATH_SEPARATOR), fieldOfRow); @@ -51,7 +51,6 @@ export module CsvRowsConversion { if (isNaN(index)) index = pathSegments[0]; if (pathSegments.length < 2) { - currentSegmentObject[index] = convertIfEmpty(val); return; } @@ -118,7 +117,7 @@ export module CsvRowsConversion { } } - if (currentField !== '') addFieldToRow(currentField, currentRow); + if (lastCharacter !== 'linebreak') addFieldToRow(currentField, currentRow); if (currentRow.length > 0) rows.push(currentRow); return rows; diff --git a/app/core/import/util.ts b/app/core/import/util.ts index 23a04a9769..1042345fbc 100644 --- a/app/core/import/util.ts +++ b/app/core/import/util.ts @@ -1,17 +1,16 @@ -import {ObjectCollection, reduce, dissoc} from 'tsfun'; -import {getOn} from 'tsfun'; - - -// @author: Daniel de Oliveira +import {ObjectCollection, reduce, dissoc, getOn} from 'tsfun'; +/** + * @author Daniel de Oliveira + */ export const makeLookup = (path: string) => { return (as: Array): ObjectCollection => { return reduce((amap: {[_:string]: A}, a: A) => { - amap[getOn(a)(path)] = a; + amap[getOn(path)(a)] = a; return amap; }, {})(as); diff --git a/app/core/model/model-util.ts b/app/core/model/model-util.ts index fa07d71c7e..071c0832c9 100644 --- a/app/core/model/model-util.ts +++ b/app/core/model/model-util.ts @@ -1,5 +1,5 @@ import {Document} from 'idai-components-2'; -import {nthOr} from 'tsfun'; +import {get} from 'tsfun'; import {ResourceId} from '../../c'; /** @@ -23,7 +23,7 @@ export module ModelUtil { const targetIds: string[]|undefined = document.resource.relations[relationName]; if (!targetIds) return undefined; - return nthOr(index, undefined as any)(targetIds); + return get(index)(targetIds) as (ResourceId|undefined); } } diff --git a/app/core/util/object-util.ts b/app/core/util/object-util.ts index 1422648e00..f599b24622 100644 --- a/app/core/util/object-util.ts +++ b/app/core/util/object-util.ts @@ -1,4 +1,5 @@ -import {clone as tsfunClone, jsonClone, setOn, getOnOr} from 'tsfun'; +import {clone as tsfunClone, jsonClone, getOn} from 'tsfun'; +import {setOn} from 'tsfun-extra'; /** * @author Thomas Kleinke @@ -21,5 +22,5 @@ export function clone(struct: T): T { */ export function takeOrMake(o: T, path: string, alternative: any) { - return setOn(o, path)(getOnOr(path , alternative)(o)); + return setOn(o, path)(getOn(path , alternative)(o)); } diff --git a/config/Config-Kalapodi.json b/config/Config-Kalapodi.json index 130b7f2ff2..83e2eb1070 100644 --- a/config/Config-Kalapodi.json +++ b/config/Config-Kalapodi.json @@ -30,7 +30,8 @@ "valuelists": { "storagePlace": "Find-storagePlace-kalapodi", "measuringPointOrigin": "Find-measuringPointOrigin-kalapodi", - "workflow": "Find-workflow-kalapodi" + "workflow": "Find-workflow-kalapodi", + "period": "periods-kalapodi" } }, "Sample": { @@ -95,10 +96,14 @@ "fields": { "imageSubject": { "inputType": "radio" + }, + "originalType": { + "inputType": "dropdown" } }, "valuelists": { - "imageSubject": "Photo-imageSubject-kalapodi" + "imageSubject": "Photo-imageSubject-kalapodi", + "originalType": "Photo-originalType-default" } }, "Metal:default": { diff --git a/config/Library/Valuelists.json b/config/Library/Valuelists.json index dfd4964a6b..cc7b6e9281 100644 --- a/config/Library/Valuelists.json +++ b/config/Library/Valuelists.json @@ -8776,5 +8776,41 @@ "Toniger Sand": {}, "Toniger Schluff": {} } + }, + "periods-kalapodi": { + "createdBy": "A. K. 2019", + "creationDate": "", + "description": { + "de": "", + "en": "" + }, + "values": { + "Archaisch": {}, + "Bronzezeitlich": {}, + "Byzantinisch": {}, + "Eisenzeitlich": {}, + "Frühbronzezeitlich": {}, + "Frühbyzantinisch": {}, + "Frühhellenistisch": {}, + "Frühkaiserzeitlich": {}, + "Frühklassisch": {}, + "Geometrisch": {}, + "Hellenistisch": {}, + "Kaiserzeitlich": {}, + "Klassisch": {}, + "Mykenisch": {}, + "Neolithisch": {}, + "Neuzeitlich": {}, + "Osmanisch": {}, + "Pleistozän": {}, + "Rezent": {}, + "Römisch": {}, + "Spätantik": {}, + "Spätbronzezeitlich": {}, + "Spätbyzantinisch": {}, + "Späthellenistisch": {}, + "Spätkaiserzeitlich": {}, + "Spätklassisch": {} + } } } \ No newline at end of file diff --git a/notarize.js b/notarize.js index b4455a80ef..f39f24cb56 100644 --- a/notarize.js +++ b/notarize.js @@ -3,7 +3,7 @@ const {notarize} = require('electron-notarize'); exports.default = async function performNotarization(context) { - // return; // comment out to enable notarization + return; // comment out to enable notarization const { electronPlatformName, appOutDir } = context; if (electronPlatformName !== 'darwin') return; diff --git a/package-lock.json b/package-lock.json index 1f37751d39..5ed503c628 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "idai-field-client", - "version": "2.14.0", + "version": "2.15.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -5793,7 +5793,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5811,11 +5812,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5828,15 +5831,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5939,7 +5945,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5949,6 +5956,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5961,17 +5969,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5988,6 +5999,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6066,7 +6078,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6076,6 +6089,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -6151,7 +6165,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -6181,6 +6196,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6198,6 +6214,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6236,11 +6253,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -6794,7 +6813,7 @@ }, "gulp": { "version": "3.9.1", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "resolved": "http://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "dev": true, "requires": { @@ -7380,9 +7399,9 @@ } }, "idai-components-2": { - "version": "1.46.7", - "resolved": "https://registry.npmjs.org/idai-components-2/-/idai-components-2-1.46.7.tgz", - "integrity": "sha512-q00V8V+QNlIjs9uYFuH29IMwU438ke8HceHUSO4CHA2duvayDKFzoGEbdV9Q5TE7ZnwaZLOVmIA2Bayn3BzrUw==", + "version": "1.46.10", + "resolved": "https://registry.npmjs.org/idai-components-2/-/idai-components-2-1.46.10.tgz", + "integrity": "sha512-FF9zWidwgjrvJ/n1XZlvKeXU0mVHB/hBm8i7zcipePyFVJYkM8djrkefooVXzNyilpe8MU5WDOR0owScPz2i2Q==", "requires": { "@angular/animations": "8.2.4", "@angular/common": "8.2.4", @@ -7404,9 +7423,9 @@ "reflect-metadata": "0.1.8", "rxjs": "6.5.3", "systemjs": "0.21.4", - "tsfun": "3.4.3", - "tsfun-core": "3.4.3", - "tsfun-extra": "3.4.3", + "tsfun": "3.5.2", + "tsfun-core": "3.5.2", + "tsfun-extra": "3.5.2", "zone.js": "0.8.26" }, "dependencies": { @@ -7442,6 +7461,11 @@ "version": "0.21.4", "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.21.4.tgz", "integrity": "sha512-l1O8boHjAIY5UG74Xk4B63LK9QbFxv/FkQa//GGGWaTeQoMhTsWnFrYwPWBScSF4xQFMO/+v9QB4i633h8Oytw==" + }, + "tsfun": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/tsfun/-/tsfun-3.5.2.tgz", + "integrity": "sha512-sOOT2TwKqa6VCoaRzgSpLIV8l4UhcgUgRBs3rNN0UZdtRJUjLkAiubXjC90fmgBfneB+qmDrL5kJMJJF1ZZZZw==" } } }, @@ -12435,6 +12459,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz", "integrity": "sha512-KUWx9UWGQD12zsmLNj64/pndaz4iJh/Pj7nopgkfDG6RlCcbMZvT6+9l7dchK4idog2Is8VdC/PvNbFuFmalIQ==", + "optional": true, "requires": { "xtend": "~4.0.0" } @@ -12459,6 +12484,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-2.1.1.tgz", "integrity": "sha512-JNMCTSchq1YtQLMGePmT07UE7hIIYR4GHpZI7+nUXbM9XgNtRAwcBGhnyJyITwpTILTkUcNPBKZ9lZmTUj2E3g==", + "optional": true, "requires": { "abstract-leveldown": "~3.0.0", "bindings": "~1.3.0", @@ -13032,6 +13058,7 @@ "version": "6.4.3", "resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-6.4.3.tgz", "integrity": "sha512-EU83ZZJjorwGL9DQZ9HAILY8D+ulX2RYVMtsCfIuzaIJEUrHh/dhSIy5854n42NBOUWug3gFDyO58w5k+64HTQ==", + "optional": true, "requires": { "inherits": "2.0.3" } @@ -16394,7 +16421,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -16437,7 +16465,8 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -16450,6 +16479,7 @@ "block-stream": { "version": "0.0.9", "bundled": true, + "optional": true, "requires": { "inherits": "~2.0.0" } @@ -16457,6 +16487,7 @@ "boom": { "version": "2.10.1", "bundled": true, + "optional": true, "requires": { "hoek": "2.x.x" } @@ -16464,6 +16495,7 @@ "brace-expansion": { "version": "1.1.8", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -16481,30 +16513,36 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "combined-stream": { "version": "1.0.5", "bundled": true, + "optional": true, "requires": { "delayed-stream": "~1.0.0" } }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "cryptiles": { "version": "2.0.5", "bundled": true, + "optional": true, "requires": { "boom": "2.x.x" } @@ -16539,7 +16577,8 @@ }, "delayed-stream": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "delegates": { "version": "1.0.0", @@ -16561,7 +16600,8 @@ }, "extsprintf": { "version": "1.3.0", - "bundled": true + "bundled": true, + "optional": true }, "forever-agent": { "version": "0.6.1", @@ -16580,11 +16620,13 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "fstream": { "version": "1.0.11", "bundled": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -16635,6 +16677,7 @@ "glob": { "version": "7.1.2", "bundled": true, + "optional": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -16646,7 +16689,8 @@ }, "graceful-fs": { "version": "4.1.11", - "bundled": true + "bundled": true, + "optional": true }, "har-schema": { "version": "1.0.5", @@ -16670,6 +16714,7 @@ "hawk": { "version": "3.1.3", "bundled": true, + "optional": true, "requires": { "boom": "2.x.x", "cryptiles": "2.x.x", @@ -16679,7 +16724,8 @@ }, "hoek": { "version": "2.16.3", - "bundled": true + "bundled": true, + "optional": true }, "http-signature": { "version": "1.1.1", @@ -16694,6 +16740,7 @@ "inflight": { "version": "1.0.6", "bundled": true, + "optional": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -16701,7 +16748,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.4", @@ -16711,6 +16759,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -16722,7 +16771,8 @@ }, "isarray": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "isstream": { "version": "0.1.2", @@ -16777,11 +16827,13 @@ }, "mime-db": { "version": "1.30.0", - "bundled": true + "bundled": true, + "optional": true }, "mime-types": { "version": "2.1.17", "bundled": true, + "optional": true, "requires": { "mime-db": "~1.30.0" } @@ -16789,17 +16841,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -16854,7 +16909,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "oauth-sign": { "version": "0.8.2", @@ -16869,6 +16925,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -16894,7 +16951,8 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "performance-now": { "version": "0.2.0", @@ -16903,7 +16961,8 @@ }, "process-nextick-args": { "version": "1.0.7", - "bundled": true + "bundled": true, + "optional": true }, "punycode": { "version": "1.4.1", @@ -16936,6 +16995,7 @@ "readable-stream": { "version": "2.3.3", "bundled": true, + "optional": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -16978,13 +17038,15 @@ "rimraf": { "version": "2.6.2", "bundled": true, + "optional": true, "requires": { "glob": "^7.0.5" } }, "safe-buffer": { "version": "5.1.1", - "bundled": true + "bundled": true, + "optional": true }, "semver": { "version": "5.4.1", @@ -17004,6 +17066,7 @@ "sntp": { "version": "1.0.9", "bundled": true, + "optional": true, "requires": { "hoek": "2.x.x" } @@ -17033,6 +17096,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -17042,6 +17106,7 @@ "string_decoder": { "version": "1.0.3", "bundled": true, + "optional": true, "requires": { "safe-buffer": "~5.1.0" } @@ -17054,6 +17119,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -17066,6 +17132,7 @@ "tar": { "version": "2.2.1", "bundled": true, + "optional": true, "requires": { "block-stream": "*", "fstream": "^1.0.2", @@ -17115,7 +17182,8 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "uuid": { "version": "3.1.0", @@ -17149,7 +17217,8 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -17995,19 +18064,19 @@ } }, "tsfun": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tsfun/-/tsfun-3.4.3.tgz", - "integrity": "sha512-s1/Bh4kisbGJjw2/VSjrFuYFi5v7myYaCnynsoTGqoj7Rygn8gr8w0j9iqj44rJtbpQBm4A5O4Y/ugkUmXxTtg==" + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/tsfun/-/tsfun-3.5.4.tgz", + "integrity": "sha512-2zIpYi8cI9NKS5rLWk4JluQag+8aaiflvHWMQuM81qmKI7praGsni/LxLruGU7e2msAMappAx3d5f3/VSHgvMw==" }, "tsfun-core": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tsfun-core/-/tsfun-core-3.4.3.tgz", - "integrity": "sha512-JQf+b309WnI8oE3LAw4IbYbAahpAiOGS1Oh3qkbB1+Kwn/uqpnn5FIwQtFcKpGF9tqiKgXRxvr6Hq645Da01WQ==" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/tsfun-core/-/tsfun-core-3.5.2.tgz", + "integrity": "sha512-c7M5pdwUm8AxganZ29GiV1tE2haT3+bsjS3QGlgZYdOGXK3qZ3EtD2vy6BVjkMrEsYYZHqQT1Im1NlY7MhGYSQ==" }, "tsfun-extra": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tsfun-extra/-/tsfun-extra-3.4.3.tgz", - "integrity": "sha512-YkqM+BahCSmNBlWAbRScZjR1ARhgnCBXSi+WDOMit9VgxD5o6j75i9H1YxfOeJkTqxq5X0qEgcMOaVFBqCsmNw==" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/tsfun-extra/-/tsfun-extra-3.5.2.tgz", + "integrity": "sha512-BbEnlJp3vLB6OVmg9CQsFosWxR/wXFDn1hqQOEhnkS6a0tneBMfY3VgEy9egAJbey9PSo/F24bKIGZ0rEbTK4A==" }, "tslib": { "version": "1.9.3", diff --git a/package.json b/package.json index 16cf9fee05..eeafcfc340 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "express": "4.16.4", "express-pouchdb": "4.1.0", "geojson-rewind": "0.3.1", - "idai-components-2": "1.46.7", + "idai-components-2": "1.46.10", "leaflet": "1.5.1", "leaflet-imageoverlay-rotated": "0.1.1", "leaflet.pm": "0.17.3", @@ -94,9 +94,9 @@ "showdown": "1.8.7", "svg-pan-zoom": "3.6.0", "systemjs": "0.21.5", - "tsfun": "3.4.3", - "tsfun-extra": "3.4.3", - "tsfun-core": "3.4.3", + "tsfun": "3.5.4", + "tsfun-extra": "3.5.2", + "tsfun-core": "3.5.2", "viz.js": "1.8.1", "zone.js": "0.8.26" }, diff --git a/test/subsystem/import.spec.ts b/test/subsystem/import.spec.ts index d09e8d13fe..7516e7c3d1 100644 --- a/test/subsystem/import.spec.ts +++ b/test/subsystem/import.spec.ts @@ -28,15 +28,15 @@ describe('Import/Subsystem', () => { it('create one operation', async done => { await Importer.doImport( - 'native', - new TypeUtility(_projectConfiguration), - datastore, - { getUsername: () => 'testuser'}, - _projectConfiguration, - undefined, - false, + 'native', + new TypeUtility(_projectConfiguration), + datastore, + { getUsername: () => 'testuser'}, + _projectConfiguration, + undefined, + false, false, - '{ "type": "Trench", "identifier" : "t1", "shortDescription" : "Our Trench 1"}', () => '101'); + '{ "type": "Trench", "identifier" : "t1", "shortDescription" : "Our Trench 1"}', () => '101'); const result = await datastore.find({}); expect(result.documents.length).toBe(1); @@ -57,7 +57,7 @@ describe('Import/Subsystem', () => { _projectConfiguration, trench.resource.id, false, false, - '{ "type": "Find", "identifier" : "obob1", "shortDescription" : "O.B. One", "geometry": { "type": "UnsupportedGeometryType", "coordinates": [1, 2] } }', + '{ "type": "Find", "identifier" : "obob1", "shortDescription" : "O.B. One", "geometry": { "type": "UnsupportedGeometryType", "coordinates": [1, 2] } }', () => '101'); expect(report.errors[0]).toEqual([ValidationErrors.UNSUPPORTED_GEOMETRY_TYPE, "UnsupportedGeometryType"]); @@ -101,7 +101,7 @@ describe('Import/Subsystem', () => { }); - it('create one find, connect to existing operation ', async done => { + it('create one find, connect to existing operation', async done => { const stored = await datastore.create({ resource: { identifier: 't1', type: 'Trench', shortDescription: 'Our Trench 1', relations: {}}}); @@ -113,7 +113,7 @@ describe('Import/Subsystem', () => { _projectConfiguration, stored.resource.id, false, false, - '{ "type": "Find", "identifier" : "f1", "shortDescription" : "Our Find 1"}', + '{ "type": "Find", "identifier" : "f1", "shortDescription" : "Our Find 1"}', () => '101'); const result = await datastore.find({}); @@ -124,7 +124,7 @@ describe('Import/Subsystem', () => { }); - it('invalid structure - dont import', async done => { + it('invalid structure - do not import', async done => { const resourceId = (await datastore.create( { resource: { identifier: 't1', type: 'Trench', shortDescription: 'Our Trench 1', relations: {}}} @@ -149,23 +149,146 @@ describe('Import/Subsystem', () => { }); - it('update shortDescription', async done => { + it('update field', async done => { + await datastore.create({ resource: { id: 'a', identifier: 'a', type: 'Trench', relations: {} }}); await datastore.create({ resource: { identifier: 'f1', type: 'Feature', shortDescription: 'feature1', relations: { isRecordedIn: ['a']}}}); await Importer.doImport( 'native', new TypeUtility(_projectConfiguration), datastore, - { getUsername: () => 'testuser'}, + { getUsername: () => 'testuser' }, + _projectConfiguration, + undefined, + true, false, + '{ "type": "Feature", "identifier" : "f1", "shortDescription" : "feature_1" }', + () => '101'); + + const result = await datastore.find({}); + expect(result.documents[1].resource.shortDescription).toBe('feature_1'); + done(); + }); + + + it('delete field', async done => { + + await datastore.create({ resource: { id: 'a', identifier: 'a', type: 'Trench', relations: {} }}); + await datastore.create({ resource: { + identifier: 'f1', + type: 'Feature', + shortDescription: 'feature1', + relations: { isRecordedIn: ['a'] } + } }); + + await Importer.doImport( + 'native', + new TypeUtility(_projectConfiguration), + datastore, + { getUsername: () => 'testuser' }, + _projectConfiguration, + undefined, + true, true, + '{ "type": "Feature", "identifier" : "f1", "shortDescription": null }', + () => '101'); + + const result = await datastore.find({}); + expect(result.documents[1].resource.shortDescription).toBeUndefined(); + done(); + }); + + + it('delete relation', async done => { + + await datastore.create({ resource: { id: 'a', identifier: 'a', type: 'Trench', relations: { } }}); + await datastore.create({ resource: { + id: 'f1', + identifier: 'f1', + type: 'Feature', + relations: { isRecordedIn: ['a'], isAfter: ['f2'] } + } }); + await datastore.create({ resource: { + id: 'f2', + identifier: 'f2', + type: 'Feature', + relations: { isRecordedIn: ['a'], isBefore: ['f1'] } + } }); + + await Importer.doImport( + 'native', + new TypeUtility(_projectConfiguration), + datastore, + { getUsername: () => 'testuser' }, + _projectConfiguration, + undefined, + true, true, + '{ "type": "Feature", "identifier" : "f1", "relations": { "isAfter": null } }', + () => '101'); + + const result = await datastore.find({}); + expect(result.documents[1].resource.relations.isAfter).toBeUndefined(); + expect(result.documents[2].resource.relations.isBefore).toBeUndefined(); + done(); + }); + + + it('do not delete field if deletions are not permitted', async done => { + + await datastore.create({ resource: { id: 'a', identifier: 'a', type: 'Trench', relations: {} }}); + await datastore.create({ resource: { + identifier: 'f1', + type: 'Feature', + shortDescription: 'feature1', + relations: { isRecordedIn: ['a'] } + } }); + + await Importer.doImport( + 'native', + new TypeUtility(_projectConfiguration), + datastore, + { getUsername: () => 'testuser' }, + _projectConfiguration, + undefined, + true, false, + '{ "type": "Feature", "identifier" : "f1", "shortDescription": null }', + () => '101'); + + const result = await datastore.find({}); + expect(result.documents[1].resource.shortDescription).toEqual('feature1'); + done(); + }); + + + it('do not delete relation if deletions are not permitted', async done => { + + await datastore.create({ resource: { id: 'a', identifier: 'a', type: 'Trench', relations: { } }}); + await datastore.create({ resource: { + id: 'f1', + identifier: 'f1', + type: 'Feature', + relations: { isRecordedIn: ['a'], isAfter: ['f2'] } + } }); + await datastore.create({ resource: { + id: 'f2', + identifier: 'f2', + type: 'Feature', + relations: { isRecordedIn: ['a'], isBefore: ['f1'] } + } }); + + await Importer.doImport( + 'native', + new TypeUtility(_projectConfiguration), + datastore, + { getUsername: () => 'testuser' }, _projectConfiguration, undefined, true, false, - '{ "type": "Feature", "identifier" : "f1", "shortDescription" : "feature_1"}', + '{ "type": "Feature", "identifier" : "f1", "relations": { "isAfter": null } }', () => '101'); const result = await datastore.find({}); - expect(result.documents[0].resource.shortDescription).toBe('feature_1'); + expect(result.documents[1].resource.relations.isAfter).toEqual(['f2']); + expect(result.documents[2].resource.relations.isBefore).toEqual(['f1']); done(); }); @@ -182,7 +305,7 @@ describe('Import/Subsystem', () => { _projectConfiguration, undefined, true, false, - '{ "type": "Feature", "identifier" : "f1", "shortDescription" : "feature_1"}' + "\n" + '{ "type": "Feature", "identifier" : "f1", "shortDescription" : "feature_1"}' + "\n" + '{ "type": "Feature", "identifier" : "notexisting", "shortDescription" : "feature_2"}', () => '101'); @@ -207,7 +330,7 @@ describe('Import/Subsystem', () => { _projectConfiguration, 'f1', false, false, - '{ "type": "Trench", "identifier" : "t2", "shortDescription" : "Our Trench 2"}', + '{ "type": "Trench", "identifier" : "t2", "shortDescription" : "Our Trench 2"}', () => '101'); expect(importReport.errors[0][0]).toEqual(ImportErrors.OPERATIONS_NOT_ALLOWED); diff --git a/test/unit/core/import/exec/complete-inverse-relations.spec.ts b/test/unit/core/import/exec/complete-inverse-relations.spec.ts index ef57e5c492..91bffc982f 100644 --- a/test/unit/core/import/exec/complete-inverse-relations.spec.ts +++ b/test/unit/core/import/exec/complete-inverse-relations.spec.ts @@ -501,7 +501,6 @@ describe('completeInverseRelations', () => { doc1.resource.relations[IS_BELOW] = ['17']; try { - await completeInverseRelations([doc1 as any], get, getInverseRelation); fail(); } catch (errWithParams) { @@ -511,20 +510,6 @@ describe('completeInverseRelations', () => { }); - it('empty relation', async done => { - - doc1.resource.relations[IS_BELOW] = []; - try { - await completeInverseRelations([doc1 as any], get, getInverseRelation); - fail(); - } catch (errWithParams) { - expect(errWithParams[0]).toEqual(E.EMPTY_RELATION); - expect(errWithParams[1]).toEqual('one'); - } - done(); - }); - - async function expectBadInterrelation(docs, err2) { try { diff --git a/test/unit/core/import/exec/default-import.spec.ts b/test/unit/core/import/exec/default-import.spec.ts index bccbd9c1d3..beb76604f8 100644 --- a/test/unit/core/import/exec/default-import.spec.ts +++ b/test/unit/core/import/exec/default-import.spec.ts @@ -1,5 +1,5 @@ import {ImportErrors as E, ImportErrors} from '../../../../../app/core/import/exec/import-errors'; -import {buildImportFunction, ImportOptions} from '../../../../../app/core/import/exec/default-import'; +import {buildImportFunction} from '../../../../../app/core/import/exec/default-import'; /** * @author Daniel de Oliveira @@ -74,15 +74,18 @@ describe('DefaultImport', () => { mockValidator.assertIsRecordedInTargetsExist.and.returnValue(Promise.resolve(undefined)); mockDatastore.find.and.returnValue(Promise.resolve({ totalCount: 1, - documents: [{ resource: { identifier: '123', id: '1' } }] + documents: [{ resource: { identifier: '123', id: '1', relations: {} } }] })); + mockDatastore.get.and.returnValue(Promise.resolve( + { resource: { identifier: '123', id: '1', relations: {} } } + )); await (buildImportFunction( mockValidator, operationTypeNames, () => undefined, () => '101', undefined, - { mergeMode: true, permitDeletions: false }))( + { mergeMode: true }))( [{ resource: { id: '1', relations: {} } } as any], mockDatastore, 'user1'); @@ -98,7 +101,7 @@ describe('DefaultImport', () => { await (buildImportFunction( mockValidator, operationTypeNames, () => undefined, - () => '101', undefined, { mergeMode: false, permitDeletions: false }))([ + () => '101', undefined, { mergeMode: false }))([ { resource: { type: 'Find', identifier: 'one', relations: { isChildOf: '0' } } } as any], mockDatastore, 'user1'); @@ -121,7 +124,7 @@ describe('DefaultImport', () => { }); - it('not well formed ', async done => { // shows that err from default-import-calc gets propagated + it('not well formed', async done => { // shows that err from default-import-calc gets propagated mockValidator.assertIsWellformed.and.callFake(() => { throw [ImportErrors.INVALID_TYPE]}); @@ -142,8 +145,7 @@ describe('DefaultImport', () => { operationTypeNames, () => undefined, () => '101', undefined, - { mergeMode: false, permitDeletions: false, - useIdentifiersInRelations: true }); // ! + { mergeMode: false, useIdentifiersInRelations: true }); // ! mockDatastore.find.and.returnValue(Promise.resolve({ totalCount: 0 })); @@ -165,9 +167,7 @@ describe('DefaultImport', () => { () => undefined, () => '101', undefined, - { mergeMode: false, - permitDeletions: false, - useIdentifiersInRelations: false}); // ! + { mergeMode: false, useIdentifiersInRelations: false}); // ! mockDatastore.find.and.returnValue(Promise.resolve({ totalCount: 0 })); diff --git a/test/unit/core/import/exec/process.spec.ts b/test/unit/core/import/exec/process.spec.ts index bfc7a6ad16..9833827ed7 100644 --- a/test/unit/core/import/exec/process.spec.ts +++ b/test/unit/core/import/exec/process.spec.ts @@ -77,9 +77,7 @@ describe('process()', () => { const result = await process([ d('nf1', 'Feature', 'newFeature', { liesWithin: ['et1'], isAfter: ['ef1']}) ], - validator, opTypeNames, get, getInverse, - {mergeMode: false, - permitDeletions: false}); + validator, opTypeNames, get, getInverse, { mergeMode: false }); expect(result[1][0].resource.relations['isBefore'][0]).toEqual('nf1'); done(); @@ -91,9 +89,7 @@ describe('process()', () => { const result = await process([ d('nf1', 'Feature', 'newFeature', { liesWithin: ['et1'] }) ], - validator, opTypeNames, get, getInverse, - {mergeMode:false, - permitDeletions: false}); + validator, opTypeNames, get, getInverse, { mergeMode: false }); const resource = result[0][0].resource; expect(resource.id).toBe('nf1'); @@ -109,9 +105,7 @@ describe('process()', () => { [ d('nf1', 'Feature', 'newFeature', { liesWithin: ['et1'] }) ], - validator, opTypeNames, get, getInverse, - {mergeMode: false, - permitDeletions: false}); + validator, opTypeNames, get, getInverse, { mergeMode: false }); const resource = result[0][0].resource; expect(resource.id).toBe('nf1'); @@ -353,7 +347,7 @@ describe('process()', () => { (document as any)['mergeTarget'] = existingFeature; const result = await process([document], - validator, opTypeNames, get, getInverse, { mergeMode: true, permitDeletions: true }); + validator, opTypeNames, get, getInverse, { mergeMode: true }); expect(result[0][0].resource.relations['isAfter'][0]).toEqual('ef2'); expect(result[1][0].resource.id).toEqual('ef2'); @@ -368,7 +362,7 @@ describe('process()', () => { (document as any)['mergeTarget'] = existingFeature2; const result = await process([document], - validator, opTypeNames, get, getInverse, { mergeMode: true, permitDeletions: true }); + validator, opTypeNames, get, getInverse, { mergeMode: true }); const resource = result[0][0].resource; expect(resource.relations[RECORDED_IN][0]).toBe('et1'); @@ -377,7 +371,7 @@ describe('process()', () => { }); - it('merge, ignore wrong relations when not setting overwrite relations', async done => { + it('merge, return error in case of an invalid relation', async done => { const document = d('nf1', 'Feature', 'existingFeature', { isAfter: 'unknown' }); (document as any)['mergeTarget'] = existingFeature; @@ -385,13 +379,13 @@ describe('process()', () => { const result = await process([document], validator, opTypeNames, get, getInverse, { mergeMode: true }); - expect(result[0].length).toBe(1); - expect(result[2]).toBeUndefined(); + expect(result[0].length).toBe(0); + expect(result[2]).not.toBeUndefined(); done(); }); - // err cases /////////////////////////////////////////////////////////////////////////////////////////////////////// + // err cases ///////////////////////////////////////////////////////////////////////////////////////////// it('assert lies within correctness', async done => { diff --git a/test/unit/core/import/parser/csv-rows-conversion.spec.ts b/test/unit/core/import/parser/csv-rows-conversion.spec.ts index 0d804f3e25..82322ba5ed 100644 --- a/test/unit/core/import/parser/csv-rows-conversion.spec.ts +++ b/test/unit/core/import/parser/csv-rows-conversion.spec.ts @@ -113,7 +113,7 @@ describe('CsvRowsConversion', () => { }); - it('empty fields on different levels', () => { + it('parse empty fields on different levels', () => { const struct = CsvRowsConversion.parse(',')( 'a\n' + @@ -136,4 +136,16 @@ describe('CsvRowsConversion', () => { expect(struct2.length).toBe(1); expect(struct2[0]['a']['b']['c']).toBe(null); }); + + + it('parse last field in file even if empty', () => { + + const struct = CsvRowsConversion.parse(',')( + 'a,b\n' + + 'Value,'); + + expect(struct.length).toBe(1); + expect(struct[0]['a']).toBe('Value'); + expect(struct[0]['b']).toBe(null); + }); }); \ No newline at end of file