@netgrif/components-core
Version:
Netgrif Application engine frontend core Angular library
579 lines • 98.1 kB
JavaScript
import { Inject, Injectable, Optional } from '@angular/core';
import { Subject } from 'rxjs';
import { TaskHandlingService } from './task-handling-service';
import { NAE_TASK_OPERATIONS } from '../models/task-operations-injection-token';
import { HttpErrorResponse } from '@angular/common/http';
import { FileField } from '../../data-fields/file-field/models/file-field';
import { FileListField } from '../../data-fields/file-list-field/models/file-list-field';
import { createTaskEventNotification } from '../../task-content/model/task-event-notification';
import { TaskEvent } from '../../task-content/model/task-event';
import { take } from 'rxjs/operators';
import { DynamicEnumerationField } from '../../data-fields/enumeration-field/models/dynamic-enumeration-field';
import { QueuedEvent } from '../../event-queue/model/queued-event';
import { AfterAction } from '../../utility/call-chain/after-action';
import { EnumerationField } from "../../data-fields/enumeration-field/models/enumeration-field";
import * as i0 from "@angular/core";
import * as i1 from "./task-request-state.service";
import * as i2 from "@ngx-translate/core";
import * as i3 from "../../logger/services/logger.service";
import * as i4 from "../../snack-bar/services/snack-bar.service";
import * as i5 from "../../resources/engine-endpoint/task-resource.service";
import * as i6 from "../../task-content/services/field-converter.service";
import * as i7 from "../../task-content/services/task-event.service";
import * as i8 from "./selected-case.service";
import * as i9 from "../../task-content/services/task-content.service";
import * as i10 from "../../utility/call-chain/call-chain.service";
import * as i11 from "../../event-queue/services/event-queue.service";
import * as i12 from "../../user/services/user-comparator.service";
import * as i13 from "../../event/services/event.service";
import * as i14 from "../../changed-fields/services/changed-fields.service";
import * as i15 from "../../actions/services/front-action.service";
/**
* Handles the loading and updating of data fields and behaviour of
* a single Task object managed by a {@link TaskContentService} instance.
*/
export class TaskDataService extends TaskHandlingService {
_taskState;
_translate;
_log;
_snackBar;
_taskResourceService;
_fieldConverterService;
_taskEvent;
_taskOperations;
_afterActionFactory;
_eventQueue;
_userComparator;
_eventService;
_changedFieldsService;
_frontActionService;
_updateSuccess$;
_dataReloadSubscription;
constructor(_taskState, _translate, _log, _snackBar, _taskResourceService, _fieldConverterService, _taskEvent, _taskOperations, _selectedCaseService, _taskContentService, _afterActionFactory, _eventQueue, _userComparator, _eventService, _changedFieldsService, _frontActionService) {
super(_taskContentService, _selectedCaseService);
this._taskState = _taskState;
this._translate = _translate;
this._log = _log;
this._snackBar = _snackBar;
this._taskResourceService = _taskResourceService;
this._fieldConverterService = _fieldConverterService;
this._taskEvent = _taskEvent;
this._taskOperations = _taskOperations;
this._afterActionFactory = _afterActionFactory;
this._eventQueue = _eventQueue;
this._userComparator = _userComparator;
this._eventService = _eventService;
this._changedFieldsService = _changedFieldsService;
this._frontActionService = _frontActionService;
this._updateSuccess$ = new Subject();
this._dataReloadSubscription = this._taskContentService.taskDataReloadRequest$.subscribe(queuedFrontendAction => {
this.initializeTaskDataFields(new AfterAction(), true);
});
}
ngOnDestroy() {
this._updateSuccess$.complete();
this._dataReloadSubscription.unsubscribe();
if (this.isTaskPresent() && this._safeTask.dataGroups) {
this._safeTask.dataGroups.forEach(group => {
if (group && group.fields) {
group.fields.forEach(field => field.destroy());
}
});
}
}
/**
* Contains information about the success or failure of backend
* calls in [updateTaskDataFields]{@link TaskDataService#updateTaskDataFields} method.
*/
get updateSuccess$() {
return this._updateSuccess$.asObservable();
}
/**
* Loads the Data Fields of an uninitialized Task from backend
* and populates the Task managed by {@link TaskContentService} with the appropriate objects.
*
* Beware that if the Task has some data already loaded this function does nothing
* and only passes `true` to the `afterAction` argument.
*
* If the task held within the {@link TaskContentService} changes before a response is received, the response will be ignored
* and the `afterAction` will not be executed.
*
* @param afterAction if the request completes successfully emits `true` into the Subject, otherwise `false` will be emitted
* @param force set to `true` if you need force reload of all task data
*/
initializeTaskDataFields(afterAction = new AfterAction(), force = false) {
this._eventQueue.scheduleEvent(new QueuedEvent(() => {
return this.isTaskPresent();
}, nextEvent => {
this.performGetDataRequest(afterAction, force, nextEvent);
}, nextEvent => {
afterAction.resolve(false);
nextEvent.resolve(false);
}));
}
/**
* Performs a `getData` request on the task currently stored in the `taskContent` service
* @param afterAction the action that should be performed after the request is processed
* @param force set to `true` if you need force reload of all task data
* @param nextEvent indicates to the event queue that the next event can be processed
*/
performGetDataRequest(afterAction, force, nextEvent) {
if (this._safeTask.dataSize > 0 && !force) {
this.sendNotification(TaskEvent.GET_DATA, true);
afterAction.resolve(true);
this._taskContentService.$shouldCreate.next(this._safeTask.dataGroups);
nextEvent.resolve(true);
return;
}
if (force) {
this._safeTask.dataSize = 0;
}
const gottenTaskId = this._safeTask.stringId;
this._taskState.startLoading(gottenTaskId);
this._taskResourceService.getData(gottenTaskId).pipe(take(1)).subscribe(dataGroups => {
this.processSuccessfulGetDataRequest(gottenTaskId, dataGroups, afterAction, nextEvent);
}, error => {
this.processErroneousGetDataRequest(gottenTaskId, error, afterAction, nextEvent);
});
}
/**
* Processes a successful outcome of a `getData` request
* @param gottenTaskId the ID of the task whose data was requested
* @param dataGroups the returned data groups of the task
* @param afterAction the action that should be performed after the request is processed
* @param nextEvent indicates to the event queue that the next event can be processed
*/
processSuccessfulGetDataRequest(gottenTaskId, dataGroups, afterAction, nextEvent) {
if (!this.isTaskRelevant(gottenTaskId)) {
this._log.debug('current task changed before the get data response could be received, discarding...');
this._taskState.stopLoading(gottenTaskId);
afterAction.complete();
nextEvent.resolve(false);
return;
}
this._taskContentService.referencedTaskAndCaseIds = {};
this._taskContentService.taskFieldsIndex = {};
this._safeTask.dataGroups = dataGroups;
if (dataGroups.length === 0) {
this._log.info('Task has no data ' + this._safeTask);
this._safeTask.dataSize = 0;
this._taskContentService.taskFieldsIndex[this._safeTask.stringId] = {};
}
else {
this._taskContentService.referencedTaskAndCaseIds[this._safeTask.caseId] = [this._safeTask.stringId];
dataGroups.forEach(group => {
const dataGroupParentCaseId = group.parentCaseId === undefined ? this._safeTask.caseId : group.parentCaseId;
const parentTaskId = group.parentTaskId === undefined ? this._safeTask.stringId : group.parentTaskId;
const parentTransitionId = group.parentTransitionId === undefined ?
this._safeTask.transitionId : group.parentTransitionId;
if (dataGroupParentCaseId !== this._safeTask.caseId) {
if (!this._taskContentService.referencedTaskAndCaseIds[dataGroupParentCaseId]) {
this._taskContentService.referencedTaskAndCaseIds[dataGroupParentCaseId] = [group.parentTaskId];
}
else {
this._taskContentService.referencedTaskAndCaseIds[dataGroupParentCaseId].push(group.parentTaskId);
}
}
else if (dataGroupParentCaseId === this._safeTask.caseId
&& parentTaskId !== this._safeTask.stringId
&& !this._taskContentService.referencedTaskAndCaseIds[dataGroupParentCaseId]?.includes(parentTaskId)) {
this._taskContentService.referencedTaskAndCaseIds[dataGroupParentCaseId].push(group.parentTaskId);
}
if (group.fields.length > 0 && !this._taskContentService.taskFieldsIndex[parentTaskId]) {
this._taskContentService.taskFieldsIndex[parentTaskId] = {};
}
if (group.fields.length > 0 && !this._taskContentService.taskFieldsIndex[parentTaskId].fields) {
this._taskContentService.taskFieldsIndex[parentTaskId].fields = {};
}
group.fields.forEach(field => {
this._taskContentService.taskFieldsIndex[parentTaskId].transitionId = parentTransitionId;
this._taskContentService.taskFieldsIndex[parentTaskId].fields[field.stringId] = field;
field.valueChanges().subscribe(() => {
if (this.wasFieldUpdated(field)) {
if (field instanceof DynamicEnumerationField) {
field.loading = true;
this.updateTaskDataFields(this._afterActionFactory.create(bool => {
field.loading = false;
}));
}
else {
this.updateTaskDataFields();
}
}
});
if (field instanceof FileField || field instanceof FileListField) {
field.changedFields$.subscribe((change) => {
this._changedFieldsService.emitChangedFields(change);
});
}
});
this._safeTask.dataSize === undefined ?
this._safeTask.dataSize = group.fields.length :
this._safeTask.dataSize += group.fields.length;
});
}
this._taskState.stopLoading(gottenTaskId);
this.sendNotification(TaskEvent.GET_DATA, true);
afterAction.resolve(true);
nextEvent.resolve(true);
this._taskContentService.$shouldCreate.next(this._safeTask.dataGroups);
this._taskContentService.$shouldCreateCounter.next(this._taskContentService.$shouldCreateCounter.getValue() + 1);
}
/**
* Processes an erroneous outcome of a `getData` request
* @param gottenTaskId the ID of the task whose data was requested
* @param error the returned error
* @param afterAction the action that should be performed after the request is processed
* @param nextEvent indicates to the event queue that the next event can be processed
*/
processErroneousGetDataRequest(gottenTaskId, error, afterAction, nextEvent) {
this._taskState.stopLoading(gottenTaskId);
this._log.debug('getting task data failed', error);
if (!this.isTaskRelevant(gottenTaskId)) {
this._log.debug('current task changed before the get data error could be received');
afterAction.complete();
nextEvent.resolve(false);
return;
}
if (error instanceof HttpErrorResponse && error.status === 500 && error.error.message && error.error.message.startsWith('Could not find task with id')) {
this._snackBar.openWarningSnackBar(this._translate.instant('tasks.snackbar.noLongerExists'));
this._taskOperations.reload();
}
else if (error instanceof Error) {
this._snackBar.openErrorSnackBar(this._translate.instant(error.message));
}
else {
this._snackBar.openErrorSnackBar(`${this._translate.instant('tasks.snackbar.noGroup')}
${this._taskContentService.task.title} ${this._translate.instant('tasks.snackbar.failedToLoad')}`);
}
this.sendNotification(TaskEvent.GET_DATA, false);
afterAction.resolve(false);
nextEvent.resolve(false);
}
/**
* Collects all changed data fields and notifies the backend of the changes.
*
* If the request is successful clears the [changed]{@link DataField#changed} flag on all data fields that were a part of the request
* and emits a {@link ChangedFields} object into this object's [changedFields$]{@link TaskDataService#changedFields$} stream.
*
* If the task held within the {@link TaskContentService} changes before a response is received, the response will be ignored
* and the `afterAction` will not be executed.
*
* @param afterAction if the request completes successfully emits `true` into the Subject, otherwise `false` will be emitted
*/
updateTaskDataFields(afterAction = new AfterAction()) {
if (!this.isTaskPresent()) {
this._log.debug('Task is not present. Update request ignored.');
afterAction.resolve(false);
return;
}
if (this._safeTask.userId === undefined) {
this._log.debug('current task is not assigned...');
afterAction.resolve(false);
return;
}
const setTaskId = this._safeTask.stringId;
if (this._safeTask.dataSize <= 0) {
afterAction.resolve(true);
return;
}
const requestContext = this.createUpdateRequestContext();
this._eventQueue.scheduleEvent(new QueuedEvent(() => this.isSetDataRequestStillValid(requestContext.body), nextEvent => {
this.performSetDataRequest(setTaskId, requestContext, afterAction, nextEvent);
}, nextEvent => {
this.revertSetDataRequest(requestContext);
nextEvent.resolve(false);
}));
}
/**
* @ignore
* Goes over all the data fields in the managed Task and if they are valid and changed adds them to the set data request
*/
createUpdateRequestContext() {
const context = {
body: {},
previousValues: {}
};
this._safeTask.dataGroups.filter(dataGroup => dataGroup.parentTaskId === undefined).forEach(dataGroup => {
dataGroup.fields.filter(field => this.wasFieldUpdated(field)).forEach(field => {
context.body[this._task.stringId] = {};
this.addFieldToSetDataRequestBody(context, this._task.stringId, field);
});
});
this._safeTask.dataGroups.filter(dataGroup => dataGroup.parentTaskId !== undefined).forEach(dataGroup => {
if (dataGroup.fields.some(field => this.wasFieldUpdated(field))) {
context.body[dataGroup.parentTaskId] = {};
}
else {
return;
}
dataGroup.fields.filter(field => this.wasFieldUpdated(field)).forEach(field => {
this.addFieldToSetDataRequestBody(context, dataGroup.parentTaskId, field);
});
});
return context;
}
addFieldToSetDataRequestBody(context, taskId, field) {
context.body[taskId][field.stringId] = {
type: this._fieldConverterService.resolveType(field),
value: this._fieldConverterService.formatValueForBackend(field, field.value)
};
context.previousValues[field.stringId] = field.previousValue;
field.changed = false;
}
isAutocompleteEnumException(field) {
return (field instanceof EnumerationField) && (field.getComponentType() === 'autocomplete') && !(field.valid || field.value === null);
}
/**
* @param field the checked field
* @returns whether the field was updated on frontend and thus the backend should be notified
*/
wasFieldUpdated(field) {
return field.initialized && field.changed && (field.valid || field.sendInvalidValues) && (!this.isAutocompleteEnumException(field));
}
/**
* Checks whether the request could still be performed by the logged user
* @param request
*/
isSetDataRequestStillValid(request) {
if (!this.isTaskPresent()) {
return false;
}
if (this._safeTask.userId === undefined) {
return false;
}
if (!this._userComparator.compareUsers(this._safeTask.userId)) {
return false;
}
const taskIdsInRequest = Object.keys(request);
for (const taskId of taskIdsInRequest) {
if (!Object.keys(this._taskContentService.taskFieldsIndex)?.includes(taskId)) {
this._log.error(`Task id ${taskId} is not present in task fields index`);
return false;
}
const fieldIdsOfRequest = Object.keys(request[taskId]);
for (const fieldId of fieldIdsOfRequest) {
const field = this._taskContentService.taskFieldsIndex[taskId].fields[fieldId];
if (field === undefined) {
this._log.error(`Unexpected state. Datafield ${fieldId} of task ${taskId} in setData request is not present in the task.`);
return false;
}
if (!field.behavior.editable) {
this._log.debug(`Field ${fieldId}, was meant to be set to
${JSON.stringify(request[taskId][fieldId])}, but is no loner editable.`);
return false;
}
}
}
return true;
}
/**
* Performs a `setData` request on the task currently stored in the `taskContent` service
* @param setTaskId ID of the task
* @param context context of the `setData` request
* @param afterAction the action that should be performed after the request is processed
* @param nextEvent indicates to the event queue that the next event can be processed
*/
performSetDataRequest(setTaskId, context, afterAction, nextEvent) {
if (Object.keys(context.body).length === 0) {
this.sendNotification(TaskEvent.SET_DATA, true);
afterAction.resolve(true);
nextEvent.resolve(true);
return;
}
this._taskState.startLoading(setTaskId);
this._taskState.startUpdating(setTaskId);
this._taskResourceService.setData(this._safeTask.stringId, context.body).pipe(take(1))
.subscribe((response) => {
if (!this.isTaskRelevant(setTaskId)) {
this._log.debug('current task changed before the set data response could be received, discarding...');
this._taskState.stopLoading(setTaskId);
this._taskState.stopUpdating(setTaskId);
afterAction.complete();
nextEvent.resolve(false);
return;
}
if (response.success) {
this.processSuccessfulSetDataRequest(setTaskId, response, afterAction, nextEvent, context);
}
else if (response.error !== undefined) {
this.processUnsuccessfulSetDataRequest(setTaskId, response, afterAction, nextEvent, context);
}
}, error => {
this.processErroneousSetDataRequest(setTaskId, error, afterAction, nextEvent, context);
});
}
/**
* Processes an unsuccessful outcome of a `setData` request
* @param setTaskId the ID of the task whose data was set
* @param response the resulting Event outcome of the set data request
* @param afterAction the action that should be performed after the request is processed
* @param nextEvent indicates to the event queue that the next event can be processed
* @param body hold the data that was sent in request
*/
processUnsuccessfulSetDataRequest(setTaskId, response, afterAction, nextEvent, context) {
if (response.error !== '') {
this._snackBar.openErrorSnackBar(this._translate.instant(response.error));
}
else {
this._snackBar.openErrorSnackBar(this._translate.instant('tasks.snackbar.failedSave'));
}
if (response.outcome) {
const outcome = response.outcome;
const changedFieldsMap = this._eventService.parseChangedFieldsFromOutcomeTree(outcome);
if (Object.keys(changedFieldsMap).length > 0) {
this._changedFieldsService.emitChangedFields(changedFieldsMap);
}
}
this.revertToPreviousValue(context);
this.clearWaitingForResponseFlag(context.body);
this.updateStateInfo(afterAction, false, setTaskId);
nextEvent.resolve(false);
this._taskOperations.reload();
}
/**
* Processes a successful outcome of a `setData` request
* @param setTaskId the ID of the task whose data was set
* @param response the resulting Event outcome of the set data request
* @param afterAction the action that should be performed after the request is processed
* @param nextEvent indicates to the event queue that the next event can be processed
* @param context hold the data that was sent in request
*/
processSuccessfulSetDataRequest(setTaskId, response, afterAction, nextEvent, context) {
const outcome = response.outcome;
const changedFieldsMap = this._eventService.parseChangedFieldsFromOutcomeTree(outcome);
const frontActions = this._eventService.parseFrontActionsFromOutcomeTree(outcome);
if (Object.keys(changedFieldsMap).length > 0) {
this._changedFieldsService.emitChangedFields(changedFieldsMap);
}
if (frontActions?.length > 0) {
this._frontActionService.runAll(frontActions);
}
this.clearWaitingForResponseFlag(context.body);
this._snackBar.openSuccessSnackBar(!!outcome.message ? outcome.message : this._translate.instant('tasks.snackbar.dataSaved'));
this.updateStateInfo(afterAction, true, setTaskId);
nextEvent.resolve(true);
}
/**
* Processes an erroneous outcome of a `setData` request
* @param setTaskId the ID of the task whose data was set
* @param error the returned error
* @param afterAction the action that should be performed after the request is processed
* @param nextEvent indicates to the event queue that the next event can be processed
* @param body hold the data that was sent in request
*/
processErroneousSetDataRequest(setTaskId, error, afterAction, nextEvent, context) {
this._log.debug('setting task data failed', error);
if (!this.isTaskRelevant(setTaskId)) {
this._log.debug('current task changed before the get data error could be received');
this._taskState.stopLoading(setTaskId);
this._taskState.stopUpdating(setTaskId);
afterAction.complete();
nextEvent.resolve(false);
return;
}
this.revertToPreviousValue(context);
this.clearWaitingForResponseFlag(context.body);
this._snackBar.openErrorSnackBar(this._translate.instant('tasks.snackbar.failedSave'));
this.updateStateInfo(afterAction, false, setTaskId);
nextEvent.resolve(false);
this._taskOperations.reload();
}
/**
* Reverts the effects of a failed `setData` request, so that the user sees current values.
* @param context the context of the failed request
*/
revertSetDataRequest(context) {
// this iteration could be improved if we had a map of all the data fields in a task
const totalCount = Object.keys(context.body).length;
let foundCount = 0;
for (const datagroup of this._safeTask.dataGroups) {
for (const field of datagroup.fields) {
if (!context.body[field.stringId]) {
continue;
}
if (this.compareBackendFormattedFieldValues(this._fieldConverterService.formatValueForBackend(field, field.value), context.body[field.stringId].value)) {
field.valueWithoutChange(context.previousValues[field.stringId]);
}
foundCount++;
if (foundCount === totalCount) {
return;
}
}
}
this._log.error(`Invalid state. Some data fields of task ${this._safeTask.stringId}, are no longer present in it!`);
}
/**
* Compares the values that are in the backend compatible format as given by the {@link FieldConverterService}
* and determines whether they are the same value, or not.
* @param current the current value (can also be called `value1` or `left`)
* @param old the new value (can also be called `value2` or `right`)
* @returns `true` if the values are the same and `false` otherwise
*/
compareBackendFormattedFieldValues(current, old) {
if (Array.isArray(current)) {
if (!Array.isArray(old)) {
throw new Error('Illegal arguments! Cannot compare array value to non-array value');
}
if (current.length !== old.length) {
return false;
}
return current.every((value, index) => old[index] === value);
}
return current === old;
}
/**
* @ignore
*
* stops loading and updating indicators, and emits the `result` value
* to both the `afterAction` and [_updateSuccess$]{@link TaskDataService#_updateSuccess$} streams.
*
* @param afterAction the call chain steam of the update data method
* @param result result of the update data request
* @param setTaskId the ID of the {@link Task}, who's state should be updated
*/
updateStateInfo(afterAction, result, setTaskId) {
this._taskState.stopLoading(setTaskId);
this._taskState.stopUpdating(setTaskId);
if (this._updateSuccess$.observers.length !== 0) {
this._updateSuccess$.next(result);
}
this.sendNotification(TaskEvent.SET_DATA, result);
afterAction.resolve(result);
}
/**
* Publishes a get/set data notification to the {@link TaskEventService}
* @param event the event that occurred to the task
* @param success whether the get/set data operation was successful or not
*/
sendNotification(event, success) {
this._taskEvent.publishTaskEvent(createTaskEventNotification(this._safeTask, event, success));
}
revertToPreviousValue(context) {
this._safeTask.dataGroups.forEach(dataGroup => {
dataGroup.fields.forEach(field => {
if (field.initialized && field.valid && Object.keys(context.previousValues)?.includes(field.stringId)) {
field.revertToPreviousValue();
}
});
});
}
clearWaitingForResponseFlag(body) {
Object.keys(body).forEach(taskId => {
Object.keys(body[taskId]).forEach(fieldId => {
this._taskContentService.taskFieldsIndex[taskId].fields[fieldId].waitingForResponse = false;
});
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TaskDataService, deps: [{ token: i1.TaskRequestStateService }, { token: i2.TranslateService }, { token: i3.LoggerService }, { token: i4.SnackBarService }, { token: i5.TaskResourceService }, { token: i6.FieldConverterService }, { token: i7.TaskEventService }, { token: NAE_TASK_OPERATIONS }, { token: i8.SelectedCaseService, optional: true }, { token: i9.TaskContentService }, { token: i10.CallChainService }, { token: i11.EventQueueService }, { token: i12.UserComparatorService }, { token: i13.EventService }, { token: i14.ChangedFieldsService }, { token: i15.FrontActionService }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TaskDataService });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TaskDataService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1.TaskRequestStateService }, { type: i2.TranslateService }, { type: i3.LoggerService }, { type: i4.SnackBarService }, { type: i5.TaskResourceService }, { type: i6.FieldConverterService }, { type: i7.TaskEventService }, { type: undefined, decorators: [{
type: Inject,
args: [NAE_TASK_OPERATIONS]
}] }, { type: i8.SelectedCaseService, decorators: [{
type: Optional
}] }, { type: i9.TaskContentService }, { type: i10.CallChainService }, { type: i11.EventQueueService }, { type: i12.UserComparatorService }, { type: i13.EventService }, { type: i14.ChangedFieldsService }, { type: i15.FrontActionService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFzay1kYXRhLnNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZXRncmlmLWNvbXBvbmVudHMtY29yZS9zcmMvbGliL3Rhc2svc2VydmljZXMvdGFzay1kYXRhLnNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLE1BQU0sRUFBRSxVQUFVLEVBQWEsUUFBUSxFQUFDLE1BQU0sZUFBZSxDQUFDO0FBQ3RFLE9BQU8sRUFBYSxPQUFPLEVBQWUsTUFBTSxNQUFNLENBQUM7QUFVdkQsT0FBTyxFQUFDLG1CQUFtQixFQUFDLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFDLG1CQUFtQixFQUFDLE1BQU0sMkNBQTJDLENBQUM7QUFFOUUsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sc0JBQXNCLENBQUM7QUFDdkQsT0FBTyxFQUFDLFNBQVMsRUFBQyxNQUFNLGdEQUFnRCxDQUFDO0FBRXpFLE9BQU8sRUFBQyxhQUFhLEVBQUMsTUFBTSwwREFBMEQsQ0FBQztBQUN2RixPQUFPLEVBQUMsMkJBQTJCLEVBQUMsTUFBTSxrREFBa0QsQ0FBQztBQUM3RixPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0scUNBQXFDLENBQUM7QUFJOUQsT0FBTyxFQUFDLElBQUksRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQ3BDLE9BQU8sRUFBQyx1QkFBdUIsRUFBQyxNQUFNLHNFQUFzRSxDQUFDO0FBRzdHLE9BQU8sRUFBQyxXQUFXLEVBQUMsTUFBTSxzQ0FBc0MsQ0FBQztBQUNqRSxPQUFPLEVBQUMsV0FBVyxFQUFDLE1BQU0sdUNBQXVDLENBQUM7QUFRbEUsT0FBTyxFQUFDLGdCQUFnQixFQUFDLE1BQU0sOERBQThELENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRzlGOzs7R0FHRztBQUVILE1BQU0sT0FBTyxlQUFnQixTQUFRLG1CQUFtQjtJQUs5QjtJQUNBO0lBQ0E7SUFD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