@medplum/mock 0.9.28 → 0.9.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ import { LoginState, MedplumClient, ProfileResource } from '@medplum/core';
2
+ import { Binary } from '@medplum/fhirtypes';
3
+ export interface MockClientOptions {
4
+ readonly debug?: boolean;
5
+ }
6
+ export declare class MockClient extends MedplumClient {
7
+ readonly debug: boolean;
8
+ activeLoginOverride?: LoginState;
9
+ constructor(clientOptions?: MockClientOptions);
10
+ clear(): void;
11
+ getProfile(): ProfileResource;
12
+ setActiveLoginOverride(activeLoginOverride: LoginState): void;
13
+ getActiveLogin(): LoginState | undefined;
14
+ createBinary(_data: any, filename: string, contentType: string): Promise<Binary>;
15
+ }
@@ -0,0 +1,2 @@
1
+ export * from './client';
2
+ export * from './mocks';
package/dist/cjs/index.js CHANGED
@@ -4,6 +4,44 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.medplum = global.medplum || {}, global.medplum.mock = {}), global.medplum.core));
5
5
  })(this, (function (exports, core) { 'use strict';
6
6
 
7
+ /******************************************************************************
8
+ Copyright (c) Microsoft Corporation.
9
+
10
+ Permission to use, copy, modify, and/or distribute this software for any
11
+ purpose with or without fee is hereby granted.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
+ PERFORMANCE OF THIS SOFTWARE.
20
+ ***************************************************************************** */
21
+
22
+ function __awaiter(thisArg, _arguments, P, generator) {
23
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
24
+ return new (P || (P = Promise))(function (resolve, reject) {
25
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
26
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
27
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
28
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
29
+ });
30
+ }
31
+
32
+ function __classPrivateFieldGet(receiver, state, kind, f) {
33
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
34
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
35
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
36
+ }
37
+
38
+ function __classPrivateFieldSet(receiver, state, value, kind, f) {
39
+ if (kind === "m") throw new TypeError("Private method is not writable");
40
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
41
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
42
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
43
+ }
44
+
7
45
  const ExampleAccessPolicy = {
8
46
  resourceType: 'AccessPolicy',
9
47
  id: '123',
@@ -7099,34 +7137,6 @@
7099
7137
  ],
7100
7138
  };
7101
7139
 
7102
- /******************************************************************************
7103
- Copyright (c) Microsoft Corporation.
7104
-
7105
- Permission to use, copy, modify, and/or distribute this software for any
7106
- purpose with or without fee is hereby granted.
7107
-
7108
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7109
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
7110
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
7111
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
7112
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
7113
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
7114
- PERFORMANCE OF THIS SOFTWARE.
7115
- ***************************************************************************** */
7116
-
7117
- function __classPrivateFieldGet(receiver, state, kind, f) {
7118
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
7119
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
7120
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
7121
- }
7122
-
7123
- function __classPrivateFieldSet(receiver, state, value, kind, f) {
7124
- if (kind === "m") throw new TypeError("Private method is not writable");
7125
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
7126
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
7127
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7128
- }
7129
-
7130
7140
  var _MemoryRepository_resources, _MemoryRepository_history;
7131
7141
  class MemoryRepository {
7132
7142
  constructor() {
@@ -7137,7 +7147,7 @@
7137
7147
  }
7138
7148
  createResource(resource) {
7139
7149
  var _a, _b;
7140
- const result = JSON.parse(JSON.stringify(resource));
7150
+ const result = core.deepClone(resource);
7141
7151
  if (!result.id) {
7142
7152
  result.id = this.generateId();
7143
7153
  }
@@ -7162,7 +7172,7 @@
7162
7172
  }
7163
7173
  __classPrivateFieldGet(this, _MemoryRepository_resources, "f")[resourceType][id] = result;
7164
7174
  __classPrivateFieldGet(this, _MemoryRepository_history, "f")[resourceType][id].push(result);
7165
- return result;
7175
+ return core.deepClone(result);
7166
7176
  }
7167
7177
  updateResource(resource) {
7168
7178
  const result = JSON.parse(JSON.stringify(resource));
@@ -7176,23 +7186,51 @@
7176
7186
  }
7177
7187
  return this.createResource(result);
7178
7188
  }
7189
+ patchResource(resourceType, id, patch) {
7190
+ var _a, _b;
7191
+ return __awaiter(this, void 0, void 0, function* () {
7192
+ const resource = this.readResource(resourceType, id);
7193
+ let patchResult;
7194
+ try {
7195
+ const { applyPatch } = yield import('fast-json-patch');
7196
+ patchResult = applyPatch(resource, patch, true);
7197
+ }
7198
+ catch (err) {
7199
+ const patchError = err;
7200
+ const message = ((_b = (_a = patchError.message) === null || _a === void 0 ? void 0 : _a.split('\n')) === null || _b === void 0 ? void 0 : _b[0]) || 'JSONPatch error';
7201
+ throw core.badRequest(message);
7202
+ }
7203
+ const patchedResource = patchResult.newDocument;
7204
+ return this.updateResource(patchedResource);
7205
+ });
7206
+ }
7179
7207
  readResource(resourceType, id) {
7180
7208
  var _a, _b;
7181
- return (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_resources, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id];
7209
+ const resource = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_resources, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id];
7210
+ if (!resource) {
7211
+ throw core.notFound;
7212
+ }
7213
+ return core.deepClone(resource);
7182
7214
  }
7183
7215
  readHistory(resourceType, id) {
7184
7216
  var _a, _b, _c;
7217
+ this.readResource(resourceType, id);
7185
7218
  return {
7186
7219
  resourceType: 'Bundle',
7187
7220
  type: 'history',
7188
7221
  entry: ((_c = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_history, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id]) !== null && _c !== void 0 ? _c : [])
7189
- .sort((version1, version2) => { var _a, _b, _c; return -((_b = (_a = version1.meta) === null || _a === void 0 ? void 0 : _a.lastUpdated) === null || _b === void 0 ? void 0 : _b.localeCompare((_c = version2.meta) === null || _c === void 0 ? void 0 : _c.lastUpdated)); })
7190
- .map((version) => ({ resource: version })),
7222
+ .reverse()
7223
+ .map((version) => ({ resource: core.deepClone(version) })),
7191
7224
  };
7192
7225
  }
7193
7226
  readVersion(resourceType, id, versionId) {
7194
7227
  var _a, _b, _c;
7195
- return (_c = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_history, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id]) === null || _c === void 0 ? void 0 : _c.find((v) => { var _a; return ((_a = v.meta) === null || _a === void 0 ? void 0 : _a.versionId) === versionId; });
7228
+ this.readResource(resourceType, id);
7229
+ const version = (_c = (_b = (_a = __classPrivateFieldGet(this, _MemoryRepository_history, "f")) === null || _a === void 0 ? void 0 : _a[resourceType]) === null || _b === void 0 ? void 0 : _b[id]) === null || _c === void 0 ? void 0 : _c.find((v) => { var _a; return ((_a = v.meta) === null || _a === void 0 ? void 0 : _a.versionId) === versionId; });
7230
+ if (!version) {
7231
+ throw core.notFound;
7232
+ }
7233
+ return core.deepClone(version);
7196
7234
  }
7197
7235
  search(searchRequest) {
7198
7236
  var _a;
@@ -7202,7 +7240,7 @@
7202
7240
  return {
7203
7241
  resourceType: 'Bundle',
7204
7242
  type: 'searchset',
7205
- entry: result.map((resource) => ({ resource })),
7243
+ entry: result.map((resource) => ({ resource: core.deepClone(resource) })),
7206
7244
  total: result.length,
7207
7245
  };
7208
7246
  }
@@ -7261,9 +7299,21 @@
7261
7299
  function matchesSearchFilterValue(resource, filter, filterValue) {
7262
7300
  const expression = filter.code.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
7263
7301
  const values = core.evalFhirPath(expression, resource);
7264
- const result = filterValue === '' ||
7265
- values.some((value) => JSON.stringify(value).toLowerCase().includes(filterValue.toLowerCase()));
7266
- return filter.operator === core.Operator.NOT_EQUALS ? !result : result;
7302
+ switch (filter.operator) {
7303
+ case core.Operator.GREATER_THAN:
7304
+ return values.some((value) => value.toString() > filterValue);
7305
+ case core.Operator.GREATER_THAN_OR_EQUALS:
7306
+ return values.some((value) => value.toString() >= filterValue);
7307
+ case core.Operator.LESS_THAN:
7308
+ return values.some((value) => value.toString() < filterValue);
7309
+ case core.Operator.LESS_THAN_OR_EQUALS:
7310
+ return values.some((value) => value.toString() <= filterValue);
7311
+ default: {
7312
+ const result = filterValue === '' ||
7313
+ values.some((value) => JSON.stringify(value).toLowerCase().includes(filterValue.toLowerCase()));
7314
+ return filter.operator === core.Operator.NOT_EQUALS ? !result : result;
7315
+ }
7316
+ }
7267
7317
  }
7268
7318
 
7269
7319
  class MockClient extends core.MedplumClient {
@@ -7320,25 +7370,27 @@
7320
7370
  this.initMockRepo();
7321
7371
  }
7322
7372
  mockFetch(url, options) {
7323
- const method = options.method || 'GET';
7324
- const path = url.replace('https://example.com/', '');
7325
- if (this.debug) {
7326
- console.log('MockClient', method, path);
7327
- }
7328
- const response = this.mockHandler(method, path, options);
7329
- if (this.debug && !response) {
7330
- console.log('MockClient: not found', method, path);
7331
- }
7332
- if (this.debug) {
7333
- console.log('MockClient', JSON.stringify(response, null, 2));
7334
- }
7335
- if ((response === null || response === void 0 ? void 0 : response.resourceType) === 'OperationOutcome' && !core.isOk(response)) {
7336
- return Promise.reject(response);
7337
- }
7338
- return Promise.resolve({
7339
- ok: true,
7340
- blob: () => Promise.resolve(response),
7341
- json: () => Promise.resolve(response),
7373
+ return __awaiter(this, void 0, void 0, function* () {
7374
+ const method = options.method || 'GET';
7375
+ const path = url.replace('https://example.com/', '');
7376
+ if (this.debug) {
7377
+ console.log('MockClient', method, path);
7378
+ }
7379
+ const response = yield this.mockHandler(method, path, options);
7380
+ if (this.debug && !response) {
7381
+ console.log('MockClient: not found', method, path);
7382
+ }
7383
+ if (this.debug) {
7384
+ console.log('MockClient', JSON.stringify(response, null, 2));
7385
+ }
7386
+ if ((response === null || response === void 0 ? void 0 : response.resourceType) === 'OperationOutcome' && !core.isOk(response)) {
7387
+ return Promise.reject(response);
7388
+ }
7389
+ return Promise.resolve({
7390
+ ok: true,
7391
+ blob: () => Promise.resolve(response),
7392
+ json: () => Promise.resolve(response),
7393
+ });
7342
7394
  });
7343
7395
  }
7344
7396
  mockCreatePdf(docDefinition, tableLayouts, fonts) {
@@ -7351,21 +7403,23 @@
7351
7403
  return Promise.resolve({});
7352
7404
  }
7353
7405
  mockHandler(method, path, options) {
7354
- if (path.startsWith('admin/')) {
7355
- return this.mockAdminHandler(method, path);
7356
- }
7357
- else if (path.startsWith('auth/')) {
7358
- return this.mockAuthHandler(method, path, options);
7359
- }
7360
- else if (path.startsWith('oauth2/')) {
7361
- return this.mockOAuthHandler(method, path, options);
7362
- }
7363
- else if (path.startsWith('fhir/R4')) {
7364
- return this.mockFhirHandler(method, path, options);
7365
- }
7366
- else {
7367
- return null;
7368
- }
7406
+ return __awaiter(this, void 0, void 0, function* () {
7407
+ if (path.startsWith('admin/')) {
7408
+ return this.mockAdminHandler(method, path);
7409
+ }
7410
+ else if (path.startsWith('auth/')) {
7411
+ return this.mockAuthHandler(method, path, options);
7412
+ }
7413
+ else if (path.startsWith('oauth2/')) {
7414
+ return this.mockOAuthHandler(method, path, options);
7415
+ }
7416
+ else if (path.startsWith('fhir/R4')) {
7417
+ return this.mockFhirHandler(method, path, options);
7418
+ }
7419
+ else {
7420
+ return null;
7421
+ }
7422
+ });
7369
7423
  }
7370
7424
  mockAdminHandler(_method, path) {
7371
7425
  if (path.startsWith('admin/projects/123')) {
@@ -7560,57 +7614,66 @@
7560
7614
  DrAliceSmithSlots.forEach((slot) => this.mockRepo.createResource(slot));
7561
7615
  }
7562
7616
  mockFhirHandler(method, url, options) {
7563
- const path = url.includes('?') ? url.substring(0, url.indexOf('?')) : url;
7564
- const match = /fhir\/R4\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?/.exec(path);
7565
- const resourceType = match === null || match === void 0 ? void 0 : match[1];
7566
- const id = match === null || match === void 0 ? void 0 : match[2];
7567
- const operation = match === null || match === void 0 ? void 0 : match[3];
7568
- const versionId = match === null || match === void 0 ? void 0 : match[4];
7569
- if (path.startsWith('fhir/R4/ValueSet/$expand')) {
7570
- return exampleValueSet;
7571
- }
7572
- else if (path === 'fhir/R4/$graphql') {
7573
- return this.mockFhirGraphqlHandler(method, path, options);
7574
- }
7575
- else if (path === 'not-found' || id === 'not-found') {
7576
- return core.notFound;
7577
- }
7578
- else if (method === 'POST') {
7579
- if (resourceType && id && operation) {
7580
- return {};
7617
+ return __awaiter(this, void 0, void 0, function* () {
7618
+ const path = url.includes('?') ? url.substring(0, url.indexOf('?')) : url;
7619
+ const match = /fhir\/R4\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?\/?([^/]+)?/.exec(path);
7620
+ const resourceType = match === null || match === void 0 ? void 0 : match[1];
7621
+ const id = match === null || match === void 0 ? void 0 : match[2];
7622
+ const operation = match === null || match === void 0 ? void 0 : match[3];
7623
+ const versionId = match === null || match === void 0 ? void 0 : match[4];
7624
+ if (path.startsWith('fhir/R4/ValueSet/$expand')) {
7625
+ return exampleValueSet;
7581
7626
  }
7582
- else if (resourceType) {
7583
- return this.mockRepo.createResource(JSON.parse(options.body));
7627
+ else if (path === 'fhir/R4/$graphql') {
7628
+ return this.mockFhirGraphqlHandler(method, path, options);
7584
7629
  }
7585
- else {
7586
- return this.mockFhirBatchHandler(method, path, options);
7587
- }
7588
- }
7589
- else if (method === 'GET') {
7590
- if (resourceType && id && versionId) {
7591
- return this.mockRepo.readVersion(resourceType, id, versionId);
7630
+ else if (path === 'not-found' || id === 'not-found') {
7631
+ return core.notFound;
7592
7632
  }
7593
- else if (resourceType && id && operation === '_history') {
7594
- return this.mockRepo.readHistory(resourceType, id);
7633
+ else if (method === 'POST') {
7634
+ if (resourceType && id && operation) {
7635
+ return core.allOk;
7636
+ }
7637
+ else if (resourceType) {
7638
+ return this.mockRepo.createResource(JSON.parse(options.body));
7639
+ }
7640
+ else {
7641
+ return this.mockFhirBatchHandler(method, path, options);
7642
+ }
7595
7643
  }
7596
- else if (resourceType && id) {
7597
- return this.mockRepo.readResource(resourceType, id);
7644
+ else if (method === 'GET') {
7645
+ if (resourceType && id && versionId) {
7646
+ return this.mockRepo.readVersion(resourceType, id, versionId);
7647
+ }
7648
+ else if (resourceType && id && operation === '_history') {
7649
+ return this.mockRepo.readHistory(resourceType, id);
7650
+ }
7651
+ else if (resourceType && id === '$csv') {
7652
+ return core.allOk;
7653
+ }
7654
+ else if (resourceType && id) {
7655
+ return this.mockRepo.readResource(resourceType, id);
7656
+ }
7657
+ else if (resourceType) {
7658
+ return this.mockRepo.search(core.parseSearchDefinition(url));
7659
+ }
7598
7660
  }
7599
- else if (resourceType) {
7600
- return this.mockRepo.search(core.parseSearchDefinition(url));
7661
+ else if (method === 'PUT') {
7662
+ return this.mockRepo.updateResource(JSON.parse(options.body));
7601
7663
  }
7602
- }
7603
- else if (method === 'PUT') {
7604
- return this.mockRepo.updateResource(JSON.parse(options.body));
7605
- }
7606
- else if (method === 'DELETE') {
7607
- if (resourceType && id) {
7608
- return this.mockRepo.deleteResource(resourceType, id);
7664
+ else if (method === 'PATCH') {
7665
+ if (resourceType && id) {
7666
+ return this.mockRepo.patchResource(resourceType, id, JSON.parse(options.body));
7667
+ }
7609
7668
  }
7610
- else {
7611
- return core.notFound;
7669
+ else if (method === 'DELETE') {
7670
+ if (resourceType && id) {
7671
+ this.mockRepo.deleteResource(resourceType, id);
7672
+ return core.allOk;
7673
+ }
7612
7674
  }
7613
- }
7675
+ throw core.notFound;
7676
+ });
7614
7677
  }
7615
7678
  mockFhirGraphqlHandler(_method, _path, options) {
7616
7679
  const { body } = options;
@@ -7633,19 +7696,37 @@
7633
7696
  return GraphQLSchemaResponse;
7634
7697
  }
7635
7698
  mockFhirBatchHandler(_method, _path, options) {
7636
- var _a;
7637
- const { body } = options;
7638
- const request = JSON.parse(body);
7639
- return {
7640
- resourceType: 'Bundle',
7641
- type: 'batch-response',
7642
- entry: (_a = request.entry) === null || _a === void 0 ? void 0 : _a.map((e) => {
7643
- var _a, _b;
7644
- const url = 'fhir/R4/' + ((_a = e === null || e === void 0 ? void 0 : e.request) === null || _a === void 0 ? void 0 : _a.url);
7645
- const method = (_b = e === null || e === void 0 ? void 0 : e.request) === null || _b === void 0 ? void 0 : _b.method;
7646
- const resource = this.mockHandler(method, url, null);
7699
+ return __awaiter(this, void 0, void 0, function* () {
7700
+ const { body } = options;
7701
+ const request = JSON.parse(body);
7702
+ let entry;
7703
+ if (request.entry) {
7704
+ entry = [];
7705
+ for (const input of request.entry) {
7706
+ entry.push(yield this.handleBatchEntry(input));
7707
+ }
7708
+ }
7709
+ return {
7710
+ resourceType: 'Bundle',
7711
+ type: 'batch-response',
7712
+ entry,
7713
+ };
7714
+ });
7715
+ }
7716
+ handleBatchEntry(input) {
7717
+ var _a, _b;
7718
+ return __awaiter(this, void 0, void 0, function* () {
7719
+ try {
7720
+ const url = 'fhir/R4/' + ((_a = input.request) === null || _a === void 0 ? void 0 : _a.url);
7721
+ const method = (_b = input.request) === null || _b === void 0 ? void 0 : _b.method;
7722
+ const resource = yield this.mockHandler(method, url, {
7723
+ body: input.resource ? JSON.stringify(input.resource) : undefined,
7724
+ });
7647
7725
  if ((resource === null || resource === void 0 ? void 0 : resource.resourceType) === 'OperationOutcome') {
7648
- return { resource, response: { status: core.getStatus(resource).toString() } };
7726
+ return {
7727
+ resource,
7728
+ response: { status: core.getStatus(resource).toString() },
7729
+ };
7649
7730
  }
7650
7731
  else if (resource) {
7651
7732
  return { resource, response: { status: '200' } };
@@ -7653,8 +7734,17 @@
7653
7734
  else {
7654
7735
  return { resource: core.notFound, response: { status: '404' } };
7655
7736
  }
7656
- }),
7657
- };
7737
+ }
7738
+ catch (err) {
7739
+ const outcome = err;
7740
+ return {
7741
+ response: {
7742
+ status: core.getStatus(outcome).toString(),
7743
+ outcome,
7744
+ },
7745
+ };
7746
+ }
7747
+ });
7658
7748
  }
7659
7749
  }
7660
7750