UNPKG

angular-odata

Version:

Client side OData typescript library for Angular

816 lines 127 kB
import { forkJoin, Observable, of, throwError } from 'rxjs'; import { defaultIfEmpty, finalize, map, switchMap } from 'rxjs/operators'; import { DEFAULT_VERSION } from '../constants'; import { ODataHelper } from '../helper'; import { ODataEntitySetResource, ODataNavigationPropertyResource, ODataPropertyResource, } from '../resources'; import { Types } from '../utils/types'; import { INCLUDE_DEEP, ODataModelEventEmitter, ODataModelEventType, ODataModelOptions, ODataModelState, } from './options'; import { ODataEntitiesAnnotations, ODataEntityAnnotations, } from '../annotations'; export class ODataCollection { static model = null; _parent = null; _resource = null; _resources = []; _annotations; _entries = []; _model; models() { return this._entries .filter((e) => e.state !== ODataModelState.Removed) .map((e) => e.model); } get length() { return this.models().length; } //Events events$; constructor(entities = [], { parent, resource, annots, model, reset = false, } = {}) { const Klass = this.constructor; if (!model && Klass.model !== null) model = Klass.model; if (!model) throw new Error('Collection: Collection need model'); this._model = model; // Events this.events$ = new ODataModelEventEmitter({ collection: this }); this.events$.subscribe((e) => model.meta.events$.emit(e)); // Parent if (parent !== undefined) { this._parent = parent; } // Resource if (this._parent === null && !resource) resource = this._model.meta.collectionResourceFactory(); if (resource) { this.attach(resource); } // Annotations this._annotations = annots || new ODataEntitiesAnnotations(ODataHelper[resource?.api?.options.version || DEFAULT_VERSION]); entities = entities || []; this.assign(entities, { reset }); } isParentOf(child) { return (child !== this && ODataModelOptions.chain(child).some((p) => p[0] === this)); } resource() { return ODataModelOptions.resource(this); } pushResource(resource) { // Push current parent and resource this._resources.push({ parent: this._parent, resource: this._resource }); // Replace parent and resource this._parent = null; this._resource = resource; } popResource() { // Pop parent and resource const pop = this._resources.pop(); if (pop !== undefined) { const current = { parent: this._parent, resource: this._resource }; this._parent = pop.parent; this._resource = pop.resource; return current; } return undefined; } attach(resource) { if (this._resource !== null && this._resource.outgoingType() !== resource.outgoingType() && !this._resource.isSubtypeOf(resource)) throw new Error(`attach: Can't reattach ${this._resource.outgoingType()} to ${resource.outgoingType()}`); this._entries.forEach(({ model }) => { const modelResource = this._model.meta.modelResourceFactory(resource.cloneQuery()); if (modelResource !== undefined) model.attach(modelResource); }); const current = this._resource; if (current === null || !current.isEqualTo(resource)) { this._resource = resource; this.events$.trigger(ODataModelEventType.Attach, { previous: current, value: resource, }); } } withResource(resource, ctx) { // Push this.pushResource(resource); // Execute const result = ctx(this); if (result instanceof Observable) { return result.pipe(finalize(() => this.popResource())); } else { // Pop this.popResource(); return result; } } asEntitySet(ctx) { // Build new resource const resource = this._model.meta.collectionResourceFactory(this._resource?.cloneQuery()); return this.withResource(resource, ctx); } annots() { return this._annotations; } modelFactory(data, { reset = false } = {}) { let Model = this._model; const annots = new ODataEntityAnnotations(this._annotations.helper); annots.update(data); if (annots?.type !== undefined && Model.meta !== null) { const schema = Model.meta.findChildOptions((o) => o.isTypeOf(annots.type))?.structuredType; if (schema !== undefined && schema.model !== undefined) // Change to child model Model = schema.model; } return new Model(data, { annots, reset, parent: [this, null], }); } toEntities({ client_id = false, include_navigation = false, include_concurrency = false, include_computed = false, include_key = true, include_id = false, include_non_field = false, changes_only = false, field_mapping = false, chain = [], } = {}) { return this._entries .filter(({ model, state }) => state !== ODataModelState.Removed && chain.every((c) => c !== model)) .map(({ model, state }) => { var changesOnly = changes_only && state !== ODataModelState.Added; return model.toEntity({ client_id, include_navigation, include_concurrency, include_computed, include_key, include_id, include_non_field, field_mapping, changes_only: changesOnly, chain: [this, ...chain], }); }); } toJson() { return this.toEntities(INCLUDE_DEEP); } hasChanged({ include_navigation } = {}) { return (this._entries.some((e) => e.state !== ODataModelState.Unchanged) || this.models().some((m) => m.hasChanged({ include_navigation }))); } clone() { return new this.constructor(this.toEntities(INCLUDE_DEEP), { resource: this.resource(), annots: this.annots(), }); } _request(obs$, mapCallback) { this.events$.trigger(ODataModelEventType.Request, { options: { observable: obs$ }, }); return obs$.pipe(map((response) => mapCallback(response)), finalize(() => this.events$.trigger(ODataModelEventType.Sync))); } fetch({ add, merge, remove, withCount, ...options } = {}) { const resource = this.resource(); if (!resource) return throwError(() => new Error('fetch: Resource is null')); const obs$ = resource instanceof ODataEntitySetResource ? resource.fetch({ withCount, ...options }) : resource.fetch({ responseType: 'entities', withCount, ...options, }); return this._request(obs$, ({ entities, annots }) => { this._annotations = annots; return entities !== null ? this.assign(entities, { reset: true, add: add ?? true, merge: merge ?? true, remove: remove ?? true, }) : []; }); } fetchAll({ add, merge, remove, withCount, ...options } = {}) { const resource = this.resource(); if (!resource) return throwError(() => new Error('fetchAll: Resource is null')); const obs$ = resource.fetchAll({ withCount, ...options }); return this._request(obs$, ({ entities, annots }) => { this._annotations = annots; return entities !== null ? this.assign(entities, { reset: true, add: add ?? true, merge: merge ?? true, remove: remove ?? true, }) : []; }); } fetchMany(top, { add, merge, remove, withCount, ...options } = {}) { const resource = this.resource(); if (!resource) return throwError(() => new Error('fetchMany: Resource is null')); resource.query((q) => remove || this.length == 0 ? q.skip().clear() : q.skip(this.length)); const obs$ = resource.fetchMany(top, { withCount, ...options }); return this._request(obs$, ({ entities, annots }) => { this._annotations = annots; return entities !== null ? this.assign(entities, { reset: true, add: add ?? true, merge: merge ?? true, remove: remove ?? false, }) : []; }); } fetchOne({ add, merge, remove, withCount, ...options } = {}) { const resource = this.resource(); if (!resource) return throwError(() => new Error('fetchOne: Resource is null')); resource.query((q) => remove || this.length == 0 ? q.skip().clear() : q.skip(this.length)); const obs$ = resource.fetchOne({ withCount, ...options }); return this._request(obs$, ({ entity, annots }) => { this._annotations = annots; return entity !== null ? this.assign([entity], { reset: true, add: add ?? true, merge: merge ?? true, remove: remove ?? false, })[0] : null; }); } /** * Save all models in the collection * @param relModel The model is relationship * @param method The method to use * @param options HttpOptions */ save({ relModel = false, method, ...options } = {}) { const resource = this.resource(); if (resource instanceof ODataPropertyResource) return throwError(() => new Error('save: Resource is ODataPropertyResource')); const toDestroyEntity = []; const toRemoveReference = []; const toDestroyContained = []; const toCreateEntity = []; const toAddReference = []; const toCreateContained = []; const toUpdateEntity = []; const toUpdateContained = []; this._entries.forEach(({ model, state }) => { if (state === ODataModelState.Removed) { if (relModel) { toDestroyEntity.push(model); } else if (!model.isNew()) { toRemoveReference.push(model); } else { toDestroyContained.push(model); } } else if (state === ODataModelState.Added) { if (relModel) { toCreateEntity.push(model); } else if (!model.isNew()) { toAddReference.push(model); } else { toCreateContained.push(model); } } else if (model.hasChanged()) { toUpdateEntity.push(model); } }); const obs$ = forkJoin([ ...toDestroyEntity.map((m) => m.asEntity((e) => e.destroy(options))), ...toRemoveReference.map((m) => (this._model.meta.api.options.deleteRefBy === 'path' ? resource.key(m.key()) : resource) .reference() .remove(this._model.meta.api.options.deleteRefBy === 'id' ? m.asEntity((e) => e.resource()) : undefined, options)), ...toDestroyContained.map((m) => m.destroy(options)), ...toCreateEntity.map((m) => m.asEntity((e) => e.save({ method: 'create', ...options }))), ...toAddReference.map((m) => resource .reference() .add(m.asEntity((e) => e.resource()), options)), ...toCreateContained.map((m) => m.save({ method: 'create', ...options })), ...toUpdateEntity.map((m) => m.asEntity((e) => e.save({ method, ...options }))), ...toUpdateContained.map((m) => m.save({ method, ...options })), ]).pipe(defaultIfEmpty(null)); return this._request(obs$, () => { this._entries = this._entries .filter((entry) => entry.state !== ODataModelState.Removed) .map((entry) => ({ ...entry, state: ODataModelState.Unchanged })); return this.models(); }); } _addServer(model, options) { const resource = this.resource(); if (resource instanceof ODataNavigationPropertyResource) { if (!model.isNew()) { // Add Reference return resource .reference() .add(model.asEntity((e) => e.resource()), options) .pipe(map(() => model)); } else { // Create Contained return resource.create(model.toEntity(), options).pipe(map(({ entity }) => { if (entity) { model.assign(entity); } return model; })); } } else if (resource instanceof ODataEntitySetResource) { return model.asEntity((e) => e.save({ method: 'create', ...options })); } else { return of(model); } } _addModel(model, { silent = false, reset = false, reparent = false, merge = false, position, } = {}) { let entry = this._findEntry(model); if (entry !== undefined && entry.state !== ODataModelState.Removed) { if (merge) { entry.model.assign(model.toEntity(INCLUDE_DEEP)); } return entry.model; } if (entry !== undefined && entry.state === ODataModelState.Removed) { const index = this._entries.indexOf(entry); this._entries.splice(index, 1); } // Create Entry entry = { state: reset ? ODataModelState.Unchanged : ODataModelState.Added, model, key: model.key(), }; // Set Parent if (reparent) model._parent = [this, null]; // Subscribe this._link(entry); // If position is undefined and the collection is sorted, find the right position if (position === undefined && this._sortBy !== null) { for (let index = 0; index < this._entries.length; index++) { if (this._compare(model, this._entries[index], this._sortBy, 0) < 0) { position = index; break; } } } // Now add if (position !== undefined) this._entries.splice(position, 0, entry); else this._entries.push(entry); if (!silent) { model.events$.trigger(ODataModelEventType.Add, { collection: this, options: { index: position }, }); } return entry.model; } add(model, { silent = false, reparent = false, server = true, merge = false, position, reset, } = {}) { const _addModel = (m, reset) => this._addModel(m, { silent, position, merge, reparent, reset }); return server ? this._request(this._addServer(model), (model) => _addModel(model, reset ?? true)) : of(_addModel(model, reset ?? false)); } _removeServer(model, options) { let resource = this.resource(); if (resource instanceof ODataNavigationPropertyResource) { if (!model.isNew()) { // Remove Reference const target = this._model.meta.api.options.deleteRefBy === 'id' ? model.asEntity((e) => e.resource()) : undefined; if (this._model.meta.api.options.deleteRefBy === 'path') { resource = resource.key(model.key()); } return resource .reference() .remove(target, options) .pipe(map(() => model)); } else { // Remove Contained return resource.destroy(options).pipe(map(() => model)); } } else if (resource instanceof ODataEntitySetResource) { return model.asEntity((e) => e.destroy(options)); } else { return of(model); } } _removeModel(model, { silent = false, reset = false, } = {}) { const entry = this._findEntry(model); if (entry === undefined || entry.state === ODataModelState.Removed) { return model; } // Now remove const index = this._entries.indexOf(entry); this._entries.splice(index, 1); if (!(reset || entry.state === ODataModelState.Added)) { // Move to end of array and mark as removed entry.state = ODataModelState.Removed; this._entries.push(entry); } // Trigger Event if (!silent) { model.events$.trigger(ODataModelEventType.Remove, { collection: this, options: { index: index }, }); } this._unlink(entry); return entry.model; } remove(model, { silent = false, server = true, reset, } = {}) { const _removeModel = (m, reset) => this._removeModel(m, { silent, reset }); return server ? this._request(this._removeServer(model), (model) => _removeModel(model, reset ?? true)) : of(_removeModel(model, reset ?? false)); } _moveModel(model, position) { const entry = this._findEntry(model); if (entry === undefined || entry.state === ODataModelState.Removed) { return model; } // Now remove const index = this._entries.indexOf(entry); this._entries.splice(index, 1); this._entries.splice(position, 0, entry); return entry.model; } create(attrs = {}, { silent = false, server = true, } = {}) { const model = this.modelFactory(attrs); return (model.isValid() && server ? model.save() : of(model)).pipe(switchMap((model) => this.add(model, { silent, server })), map(() => model)); } set(path, value, {}) { const pathArray = (Types.isArray(path) ? path : path.match(/([^[.\]])+/g)); if (pathArray.length === 0) return undefined; if (pathArray.length > 1) { const model = this._entries[Number(pathArray[0])].model; return model.set(pathArray.slice(1), value, {}); } if (pathArray.length === 1 && ODataModelOptions.isModel(value)) { const models = this.models(); const index = Number(pathArray[0]); models[index] = value; this.assign(models, { reparent: true }); return value; } } get(path) { const pathArray = (Types.isArray(path) ? path : `${path}`.match(/([^[.\]])+/g)); if (pathArray.length === 0) return undefined; const value = this.models()[Number(pathArray[0])]; if (pathArray.length > 1 && ODataModelOptions.isModel(value)) { return value.get(pathArray.slice(1)); } return value; } has(path) { const pathArray = (Types.isArray(path) ? path : `${path}`.match(/([^[.\]])+/g)); if (pathArray.length === 0) return false; const value = this.models()[Number(pathArray[0])]; if (pathArray.length > 1 && ODataModelOptions.isModel(value)) { return value.has(pathArray.slice(1)); } return value !== undefined; } reset({ path, silent = false, } = {}) { let toAdd = []; let toChange = []; let toRemove = []; if (path !== undefined) { // Reset by path const pathArray = (Types.isArray(path) ? path : `${path}`.match(/([^[.\]])+/g)); const index = Number(pathArray[0]); if (!Number.isNaN(index)) { const model = this.models()[index]; if (ODataModelOptions.isModel(model)) { const entry = this._findEntry(model); if (entry.state === ODataModelState.Unchanged && entry.model.hasChanged()) { toChange = [entry]; } path = pathArray.slice(1); } } } else { // Reset all toAdd = this._entries.filter((e) => e.state === ODataModelState.Removed); toChange = this._entries.filter((e) => e.state === ODataModelState.Unchanged && e.model.hasChanged()); toRemove = this._entries.filter((e) => e.state === ODataModelState.Added); } toRemove.forEach((entry) => { this._removeModel(entry.model, { silent }); }); toAdd.forEach((entry) => { this._addModel(entry.model, { silent }); }); toChange.forEach((entry) => { entry.model.reset({ path, silent }); entry.state = ODataModelState.Unchanged; }); if (!silent && (toAdd.length > 0 || toRemove.length > 0 || toChange.length > 0)) { this.events$.trigger(ODataModelEventType.Reset, { options: { added: toAdd.map((e) => e.model), removed: toRemove.map((e) => e.model), changed: toChange.map((e) => e.model), }, }); } } clear({ silent = false } = {}) { const toRemove = this.models(); toRemove.forEach((m) => { this._removeModel(m, { silent }); }); this._entries = []; if (!silent) { this.events$.trigger(ODataModelEventType.Update, { options: { removed: toRemove }, }); } } assign(objects, { add = true, merge = true, remove = true, reset = false, reparent = false, silent = false, } = {}) { const offset = remove ? 0 : this.length; const models = []; const toAdd = []; const toMerge = []; const toRemove = []; objects.forEach((obj, index) => { const model = ODataModelOptions.isModel(obj) ? obj : this.modelFactory(obj, { reset, }); const position = index + offset; // Try find entry const entry = this._findEntry(model); if (merge && entry !== undefined) { if (entry.model !== model) { entry.model.assign(model.toEntity({ client_id: true, ...INCLUDE_DEEP, }), { add, merge, remove, reset, silent }); // Model Change? if (entry.model.hasChanged()) toMerge.push(entry.model); } if (reset) entry.state = ODataModelState.Unchanged; if (!models.includes(entry.model)) { models.push(entry.model); } } else if (add) { // Add toAdd.push(model); this._addModel(model, { silent, reset, reparent, position }); models.push(model); } }); if (remove) { [...this._entries].forEach((entry) => { const model = entry.model; if (!models.includes(model)) { this._removeModel(model, { silent, reset }); toRemove.push(model); } }); } if (this.models() .slice(offset) .some((m, i) => m !== models[i])) { models.forEach((m, i) => this._moveModel(m, i)); this.events$.trigger(ODataModelEventType.Sort); } if (!silent && (toAdd.length > 0 || toRemove.length > 0 || toMerge.length > 0)) { this.events$.trigger(reset ? ODataModelEventType.Reset : ODataModelEventType.Update, { options: { added: toAdd, removed: toRemove, merged: toMerge, }, }); } return models; } query(ctx) { const resource = this.resource(); if (resource) { resource.query(ctx); this.attach(resource); } return this; } callFunction(name, params, responseType, options = {}) { const resource = this.resource(); if (!(resource instanceof ODataEntitySetResource)) return throwError(() => new Error("callFunction: Can't call function without ODataEntitySetResource")); const func = resource.function(name).query((q) => q.restore(options)); switch (responseType) { case 'property': return this._request(func.callProperty(params, options), (resp) => resp); case 'model': return this._request(func.callModel(params, options), (resp) => resp); case 'collection': return this._request(func.callCollection(params, options), (resp) => resp); default: return this._request(func.call(params, { responseType, ...options }), (resp) => resp); } } callAction(name, params, responseType, options = {}) { const resource = this.resource(); if (!(resource instanceof ODataEntitySetResource)) { return throwError(() => new Error(`callAction: Can't call action without ODataEntitySetResource`)); } const action = resource.action(name).query((q) => q.restore(options)); switch (responseType) { case 'property': return this._request(action.callProperty(params, options), (resp) => resp); case 'model': return this._request(action.callModel(params, options), (resp) => resp); case 'collection': return this._request(action.callCollection(params, options), (resp) => resp); default: return this._request(action.call(params, { responseType, ...options }), (resp) => resp); } } _unlink(entry) { if (entry.subscription) { entry.subscription.unsubscribe(); entry.subscription = undefined; } } _link(entry) { if (entry.subscription) { throw new Error('Collection: Subscription already exists'); } entry.subscription = entry.model.events$.subscribe((event) => { if (event.canContinueWith(this)) { if (event.model === entry.model) { if (event.type === ODataModelEventType.Destroy) { this._removeModel(entry.model, { reset: true }); } else if (event.type === ODataModelEventType.Change && event.options?.key) { entry.key = entry.model.key(); } } const index = event.options?.index ?? this.models().indexOf(entry.model); this.events$.emit(event.push(this, index)); } }); } _findEntry(model) { return this._entries.find((entry) => (entry.key !== undefined && model.key() !== undefined && Types.isEqual(entry.key, model.key())) || entry.model.equals(model)); } // Collection functions equals(other) { return this === other; } get [Symbol.toStringTag]() { return 'Collection'; } [Symbol.iterator]() { let pointer = 0; const models = this.models(); return { next() { return { done: pointer === models.length, value: models[pointer++], }; }, }; } filter(predicate, thisArg) { return this.models().filter(predicate, thisArg); } map(callbackfn, thisArg) { return this.models().map(callbackfn, thisArg); } find(predicate) { return this.models().find(predicate); } reduce(callbackfn, initialValue) { return this.models().reduce(callbackfn, initialValue); } first() { return this.models()[0]; } last() { const models = this.models(); return models[models.length - 1]; } next(model) { const index = this.indexOf(model); if (index === -1 || index === this.length - 1) { return undefined; } return this.get(index + 1); } prev(model) { const index = this.indexOf(model); if (index <= 0) { return undefined; } return this.get(index - 1); } every(predicate) { return this.models().every(predicate); } some(predicate) { return this.models().some(predicate); } includes(model, start = 0) { return this.some((m, i) => i >= start && m.equals(model)); } indexOf(model) { const models = this.models(); const m = models.find((m) => m.equals(model)); return !m ? -1 : models.indexOf(m); } forEach(predicate, thisArg) { return this.models().forEach(predicate, thisArg); } isEmpty() { // Local length == 0 and if exist remote count is 0 or undefined return this.length === 0 && !this.annots().count; } //#region Sort _compare(e1, e2, by, index) { const m1 = ODataModelOptions.isModel(e1) ? e1 : e1.model; const m2 = ODataModelOptions.isModel(e2) ? e2 : e2.model; const value1 = m1.get(by[index].field); const value2 = m2.get(by[index].field); let result = 0; if (value1 == null && value2 != null) result = -1; else if (value1 != null && value2 == null) result = 1; else if (value1 == null && value2 == null) result = 0; else if ((typeof value1 == 'string' || value1 instanceof String) && value1.localeCompare && value1 != value2) result = value1.localeCompare(value2); else if (value1 == value2) return by.length - 1 > index ? this._compare(e1, e2, by, index + 1) : 0; else if (by[index].comparator !== undefined) result = by[index].comparator(value1, value2); else { result = value1 < value2 ? -1 : 1; } return (by[index].order ?? 1) * result; } _sortBy = null; isSorted() { return this._sortBy !== null; } sort(by, { silent } = {}) { this._sortBy = by; this._entries = this._entries.sort((e1, e2) => this._compare(e1, e2, by, 0)); if (!silent) { this.events$.trigger(ODataModelEventType.Sort); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29sbGVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2FuZ3VsYXItb2RhdGEvc3JjL2xpYi9tb2RlbHMvY29sbGVjdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsVUFBVSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVELE9BQU8sRUFBRSxjQUFjLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMxRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDeEMsT0FBTyxFQUdMLHNCQUFzQixFQUV0QiwrQkFBK0IsRUFFL0IscUJBQXFCLEdBR3RCLE1BQU0sY0FBYyxDQUFDO0FBRXRCLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV2QyxPQUFPLEVBQ0wsWUFBWSxFQUlaLHNCQUFzQixFQUN0QixtQkFBbUIsRUFFbkIsaUJBQWlCLEVBQ2pCLGVBQWUsR0FDaEIsTUFBTSxXQUFXLENBQUM7QUFDbkIsT0FBTyxFQUNMLHdCQUF3QixFQUN4QixzQkFBc0IsR0FDdkIsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixNQUFNLE9BQU8sZUFBZTtJQUcxQixNQUFNLENBQUMsS0FBSyxHQUE2QixJQUFJLENBQUM7SUFDOUMsT0FBTyxHQUtJLElBQUksQ0FBQztJQUNoQixTQUFTLEdBSUUsSUFBSSxDQUFDO0lBQ2hCLFVBQVUsR0FZSixFQUFFLENBQUM7SUFDVCxZQUFZLENBQStCO0lBQzNDLFFBQVEsR0FBNEIsRUFBRSxDQUFDO0lBQ3ZDLE1BQU0sQ0FBb0I7SUFFMUIsTUFBTTtRQUNKLE9BQU8sSUFBSSxDQUFDLFFBQVE7YUFDakIsTUFBTSxDQUFDLENBQUMsQ0FB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