diff --git a/projects/laji/src/app/shared-modules/spreadsheet/importer/importer.component.ts b/projects/laji/src/app/shared-modules/spreadsheet/importer/importer.component.ts
index 37b3b6ab2..3e2c3ff6f 100644
--- a/projects/laji/src/app/shared-modules/spreadsheet/importer/importer.component.ts
+++ b/projects/laji/src/app/shared-modules/spreadsheet/importer/importer.component.ts
@@ -12,7 +12,6 @@ import {
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { forkJoin, from as ObservableFrom, Observable, of } from 'rxjs';
-import { Document } from '../../../shared/model/Document';
import { FormService } from '../../../shared/service/form.service';
import { IFormField, VALUE_IGNORE } from '../model/excel';
import { CombineToDocument, IDocumentData, ImportService } from '../service/import.service';
@@ -32,9 +31,14 @@ import { FileService, instanceOfFileLoad } from '../service/file.service';
import { IUserMappingFile, MappingFileService } from '../service/mapping-file.service';
import { Form } from '../../../shared/model/Form';
import { Logger } from '../../../shared/logger';
-import { DocumentJobPayload } from '../../../shared/api/DocumentApi';
import { toHtmlSelectElement } from '../../../shared/service/html-element.service';
-import {ModalRef, ModalService} from 'projects/laji-ui/src/lib/modal/modal.service';
+import { ModalRef, ModalService } from 'projects/laji-ui/src/lib/modal/modal.service';
+
+import type { components } from 'projects/laji-api-client-b/generated/api';
+
+type Document = components['schemas']['document'];
+type PublicityRestrictions = Document['publicityRestrictions'];
+type BatchJob = components['schemas']['BatchJobValidationStatusResponse'];
@Component({
selector: 'laji-importer',
@@ -72,7 +76,7 @@ export class ImporterComponent implements OnInit, OnDestroy {
header?: {[key: string]: string};
fields?: {[key: string]: IFormField};
dataColumns?: ImportTableColumn[];
- jobPayload?: DocumentJobPayload;
+ job?: BatchJob;
docCnt = 0;
origColMap?: {[key: string]: string};
colMap?: {[key: string]: string};
@@ -83,8 +87,8 @@ export class ImporterComponent implements OnInit, OnDestroy {
mimeType?: string;
errors: any;
valid = false;
- priv = Document.PublicityRestrictionsEnum.publicityRestrictionsPrivate;
- publ = Document.PublicityRestrictionsEnum.publicityRestrictionsPublic;
+ priv: PublicityRestrictions = 'MZ.publicityRestrictionsPrivate';
+ publ: PublicityRestrictions = 'MZ.publicityRestrictionsPublic';
excludedFromCopy: string[] = [];
userMappings: any;
separator = MappingService.valueSplitter;
@@ -456,9 +460,9 @@ export class ImporterComponent implements OnInit, OnDestroy {
ObservableFrom(rowData).pipe(
concatMap(data => this.augmentService.augmentDocument(data.document, this.excludedFromCopy)),
toArray(),
- switchMap(documents => this.importService.validateData(documents)),
- tap(job => this.jobPayload = job),
- switchMap(job => this.importService.waitToComplete('validate', job, (status) => {
+ switchMap(documents => this.importService.startBatchJob(documents)),
+ tap(job => this.job = job),
+ switchMap(job => this.importService.waitToComplete(job, (status) => {
this.current = status.processed;
this.cdr.markForCheck();
})),
@@ -508,7 +512,7 @@ export class ImporterComponent implements OnInit, OnDestroy {
);
}
- save(publicityRestrictions: Document.PublicityRestrictionsEnum) {
+ save(publicityRestrictions: PublicityRestrictions) {
this.spreadsheetFacade.goToStep(Step.importing);
this.showOnlyErroneous = false;
let success = true;
@@ -522,14 +526,14 @@ export class ImporterComponent implements OnInit, OnDestroy {
const rowData = this.parsedData!.filter(data => data.document !== null);
this.importService.sendData({
- ...this.jobPayload,
- dataOrigin: [Document.DataOriginEnum.dataOriginSpreadsheetFile],
+ ...this.job,
+ dataOrigin: 'MY.dataOriginSpreadsheetFile',
publicityRestrictions
} as any).pipe(
- switchMap(() => this.importService.waitToComplete('create', this.jobPayload as any, (status) => {
+ switchMap(() => this.importService.waitToComplete(this.job!, (status) => {
ticker += add;
this.current = status.processed === this.total ?
- status.processed :
+ status.processed:
Math.min(Math.max(this.total - 1, 0), ticker);
this.cdr.markForCheck();
})),
diff --git a/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.html b/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.html
index a9bc923ea..5c165b499 100644
--- a/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.html
+++ b/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.html
@@ -1,5 +1,5 @@
-
{{ fields[data.field].fullLabel }}
+
{{ data.title }}
diff --git a/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.ts b/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.ts
index 07ff03f51..27b516059 100644
--- a/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.ts
+++ b/projects/laji/src/app/shared-modules/spreadsheet/importer/status-cell/error-list/error-list.component.ts
@@ -1,97 +1,81 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
import { IFormField } from '../../../model/excel';
import { TranslateService } from '@ngx-translate/core';
import { Util } from '../../../../../shared/service/util.service';
+interface ErrorGroup {
+ title: string;
+ errors: string[];
+}
+
@Component({
selector: 'laji-error-list',
templateUrl: './error-list.component.html',
styleUrls: ['./error-list.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
-export class ErrorListComponent {
+export class ErrorListComponent implements OnChanges {
@Input({ required: true }) fields!: {[key: string]: IFormField};
+ @Input() errors: unknown;
- _errors: {field: string; errors: string[]}[] = [];
+ _errors: ErrorGroup[] = [];
constructor(private translateService: TranslateService) { }
- @Input()
- set errors(data: unknown) {
- const errors = [];
+ ngOnChanges() {
+ this._errors = this.processErrors(this.errors, this.fields);
+ }
+
+ processErrors(data: unknown, fields: {[key: string]: IFormField}): ErrorGroup[] {
+ const errors: ErrorGroup[] = [];
+
if (Util.isObject(data)) {
if (Util.hasOwnProperty(data, 'status')) {
switch (data.status) {
case 403:
errors.push({
- field: 'id',
+ title: this.translateService.instant('error'),
errors: [this.translateService.instant('form.permission.no-access')]
});
break;
case 422:
default:
errors.push({
- field: 'id',
- errors: [Util.hasOwnProperty(data, 'statusText') ? data.statusText : this.translateService.instant('haseka.form.genericError')]
+ title: this.translateService.instant('error'),
+ errors: [this.translateService.instant('haseka.form.genericError')]
});
}
} else {
Object.keys(data).forEach(field => {
errors.push({
- field: this.pathToKey(field),
- errors: Array.isArray(data[field]) ? data[field] : this.pickErrors(data[field])
+ title: this.getFieldName(field, fields),
+ errors: data[field] as string[]
});
});
}
- } else if (Array.isArray(data)) {
- data.forEach(err => {
- if (typeof err !== 'object' || !Util.hasOwnProperty(err, 'dataPath')) {
- return;
- }
- errors.push({
- field: err.dataPath
- .substring(err.dataPath.substring(0, 1) === '/' ? 1 : 0)
- .replace(/\/[0-9]+/g, '[*]')
- .replace(/\//g, '.'),
- errors: [this.getMessage(err)]
- });
- });
}
- this._errors = errors;
+
+ return errors;
}
- private pathToKey(path: string) {
+ private getFieldName(path: string, fields: {[key: string]: IFormField}): string {
if (path.substring(0, 1) === '.') {
path = path.substring(1);
}
- return path.replace(/\[[0-9]+]/g, '[*]');
- }
- private pickErrors(value: any): any[] {
- if (typeof value === 'string') {
- return [value];
- } else if (Array.isArray(value)) {
- return value;
- } else if (typeof value === 'object') {
- return Object.keys((value)).reduce((prev, current) => [...prev, ...this.pickErrors(current)] as any, []);
- }
- return [value];
- }
+ const key = path.replace(/\[[0-9]+]/g, '[*]');
- private getMessage(err: unknown): string {
- if (!Util.isObject(err) || !Util.hasOwnProperty(err, 'message') || typeof err.message !== 'string') {
- return this.translateService.instant('haseka.form.genericError');
+ if (fields[key]) {
+ return fields[key].fullLabel;
}
- let base = err.message;
- if (Util.hasOwnProperty(err, 'params') && typeof err.params === 'object' && err.params && !Array.isArray(err.params)) {
- const info = Object.values(err.params);
- if (info.length) {
- base += ` '${info.join('\', \'')}'`;
- }
+
+ const arrayKey = key + '[*]';
+
+ if (fields[arrayKey]) {
+ return fields[arrayKey].fullLabel;
}
- return base;
+ return '';
}
-
}
diff --git a/projects/laji/src/app/shared-modules/spreadsheet/service/augment.service.ts b/projects/laji/src/app/shared-modules/spreadsheet/service/augment.service.ts
index 568a4ea71..4ec0d750d 100644
--- a/projects/laji/src/app/shared-modules/spreadsheet/service/augment.service.ts
+++ b/projects/laji/src/app/shared-modules/spreadsheet/service/augment.service.ts
@@ -4,9 +4,11 @@ import { from as ObservableFrom, Observable, of as ObservableOf } from 'rxjs';
import { NamedPlace } from '../../../shared/model/NamedPlace';
import { NamedPlaceApi } from '../../../shared/api/NamedPlaceApi';
import { UserService } from '../../../shared/service/user.service';
-import { Document } from '../../../shared/model/Document';
import { DocumentService } from '../../own-submissions/service/document.service';
import { MappingService } from './mapping.service';
+import type { components } from 'projects/laji-api-client-b/generated/api';
+
+type Document = components['schemas']['document'];
@Injectable()
export class AugmentService {
diff --git a/projects/laji/src/app/shared-modules/spreadsheet/service/import.service.ts b/projects/laji/src/app/shared-modules/spreadsheet/service/import.service.ts
index 9c13b9d2b..0d680ab74 100644
--- a/projects/laji/src/app/shared-modules/spreadsheet/service/import.service.ts
+++ b/projects/laji/src/app/shared-modules/spreadsheet/service/import.service.ts
@@ -1,9 +1,5 @@
import { Injectable } from '@angular/core';
-import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
-import { DocumentApi, DocumentJobPayload } from '../../../shared/api/DocumentApi';
-import { Document } from '../../../shared/model/Document';
-import { UserService } from '../../../shared/service/user.service';
import {
IFormField,
LEVEL_DOCUMENT,
@@ -15,7 +11,11 @@ import {
import { MappingService } from './mapping.service';
import * as Hash from 'object-hash';
import { catchError, delay, switchMap } from 'rxjs/operators';
-import { ArrayType } from '@angular/compiler';
+import { LajiApiClientBService } from 'projects/laji-api-client-b/src/laji-api-client-b.service';
+import type { components } from 'projects/laji-api-client-b/generated/api';
+
+type Document = components['schemas']['document'];
+type BatchJob = components['schemas']['BatchJobValidationStatusResponse'];
export interface IData {
rowIdx: number;
@@ -74,9 +74,7 @@ export class ImportService {
constructor(
private mappingService: MappingService,
- private documentApi: DocumentApi,
- private userService: UserService,
- private translateService: TranslateService
+ private api: LajiApiClientBService
) { }
hasInvalidValue(value: unknown, field: IFormField) {
@@ -84,46 +82,36 @@ export class ImportService {
return Array.isArray(mappedValue) ? mappedValue.indexOf(null) > -1 : mappedValue === null;
}
- validateData(document: Document|Document[]): Observable
{
- return this.documentApi.validate(document, {
- personToken: this.userService.getToken(),
- lang: this.translateService.currentLang,
- validationErrorFormat: 'jsonPath'
- });
+ startBatchJob(documents: Document[]) {
+ return this.api.post('/documents/batch', undefined, documents);
}
- waitToComplete(type: keyof Pick, jobPayload: DocumentJobPayload, processCB: (status: JobStatus) => void): Observable {
- const personToken = this.userService.getToken();
- const source$ = type === 'validate' ?
- this.documentApi.validate(jobPayload, {personToken}) :
- this.documentApi.create(jobPayload, personToken);
- return source$.pipe(
+ waitToComplete(job: BatchJob, processCB: (status: BatchJob['status']) => void): Observable {
+ return this.api.get('/documents/batch/{jobID}', { path: { jobID: job.id }, query: { validationErrorFormat: 'dotNotation' } }).pipe(
switchMap(response => {
processCB(response.status);
- if (response.status.percentage === 100) {
+ if (!['VALIDATING', 'COMPLETING'].includes(response.phase)) {
return of(response);
}
return of(response).pipe(
delay(1000),
- switchMap(() => this.waitToComplete(type, jobPayload, processCB))
+ switchMap(() => this.waitToComplete(job, processCB))
);
}),
catchError((e) => {
console.log('ERROR', e);
return of(e).pipe(
delay(1000),
- switchMap(() => this.waitToComplete(type, jobPayload, processCB))
+ switchMap(() => this.waitToComplete(job, processCB))
);
})
);
}
sendData(
- job: DocumentJobPayload
+ job: BatchJob
): Observable {
- return this.documentApi.create(job, this.userService.getToken(), {
- lang: this.translateService.currentLang
- });
+ return this.api.post('/documents/batch/{jobID}', { path: { jobID: job.id } });
}
flatFieldsToDocuments(
@@ -287,7 +275,7 @@ export class ImportService {
const docs: {[hash: string]: IDocumentData} = {};
Object.keys(documents).forEach(hash => {
if (!docs[hash]) {
- docs[hash] = {document: {formID}, ref: {[hash]: {}}, rows: {}, skipped: []};
+ docs[hash] = {document: {formID} as Document, ref: {[hash]: {}}, rows: {}, skipped: []};
const docData = this.findDocumentData(documents[hash]);
docs[hash].rows[(docData as any).rowIdx] = true;
if (ignoreRowsWithNoCount && !this.hasCountValue((docData as any).data) && combineBy === CombineToDocument.none) {