merchi_sdk_ts 0.0.1

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.
Files changed (226) hide show
  1. package/.babelrc +13 -0
  2. package/.eslintrc.cjs +43 -0
  3. package/LICENSE +21 -0
  4. package/README.md +2 -0
  5. package/jest.config.js +17 -0
  6. package/package.json +44 -0
  7. package/src/constants/auto_assign_production_on_actions.ts +6 -0
  8. package/src/constants/call_to_actions.test.ts +5 -0
  9. package/src/constants/call_to_actions.ts +14 -0
  10. package/src/constants/discount_types.test.ts +5 -0
  11. package/src/constants/discount_types.ts +3 -0
  12. package/src/constants/domain_types.test.ts +5 -0
  13. package/src/constants/domain_types.ts +12 -0
  14. package/src/constants/errors.ts +32 -0
  15. package/src/constants/event_types.test.ts +5 -0
  16. package/src/constants/event_types.ts +5 -0
  17. package/src/constants/field_types.test.ts +5 -0
  18. package/src/constants/field_types.ts +13 -0
  19. package/src/constants/input_types.test.ts +5 -0
  20. package/src/constants/input_types.ts +13 -0
  21. package/src/constants/inventory_status.test.ts +5 -0
  22. package/src/constants/inventory_statuses.ts +6 -0
  23. package/src/constants/invoice_types.test.ts +5 -0
  24. package/src/constants/invoice_types.ts +5 -0
  25. package/src/constants/item_types.test.ts +5 -0
  26. package/src/constants/item_types.ts +5 -0
  27. package/src/constants/job/drafting_status.test.ts +5 -0
  28. package/src/constants/job/drafting_status.ts +7 -0
  29. package/src/constants/job/payment_status.test.ts +5 -0
  30. package/src/constants/job/payment_status.ts +7 -0
  31. package/src/constants/job/production_status.test.ts +5 -0
  32. package/src/constants/job/production_status.ts +12 -0
  33. package/src/constants/job/shipment_status.test.ts +5 -0
  34. package/src/constants/job/shipment_status.ts +11 -0
  35. package/src/constants/job/supply_chain_request_status.test.ts +5 -0
  36. package/src/constants/job/supply_chain_request_status.ts +6 -0
  37. package/src/constants/job_priorities.test.ts +5 -0
  38. package/src/constants/job_priorities.ts +6 -0
  39. package/src/constants/job_status.test.ts +5 -0
  40. package/src/constants/job_status.ts +49 -0
  41. package/src/constants/job_types.test.ts +5 -0
  42. package/src/constants/job_types.ts +15 -0
  43. package/src/constants/notification_sections.test.ts +5 -0
  44. package/src/constants/notification_sections.ts +13 -0
  45. package/src/constants/notification_types.test.ts +5 -0
  46. package/src/constants/notification_types.ts +97 -0
  47. package/src/constants/notification_urgencies.test.ts +5 -0
  48. package/src/constants/notification_urgencies.ts +7 -0
  49. package/src/constants/payment_types.test.ts +5 -0
  50. package/src/constants/payment_types.ts +10 -0
  51. package/src/constants/platform.test.ts +15 -0
  52. package/src/constants/platform.ts +6 -0
  53. package/src/constants/product_types.test.ts +6 -0
  54. package/src/constants/product_types.ts +25 -0
  55. package/src/constants/rights.test.ts +5 -0
  56. package/src/constants/rights.ts +5 -0
  57. package/src/constants/robots_meta_directives.test.ts +5 -0
  58. package/src/constants/robots_meta_directives.ts +11 -0
  59. package/src/constants/roles.test.ts +5 -0
  60. package/src/constants/roles.ts +38 -0
  61. package/src/constants/shipment_companies.test.ts +5 -0
  62. package/src/constants/shipment_companies.ts +18 -0
  63. package/src/constants/shipment_services.test.ts +5 -0
  64. package/src/constants/shipment_services.ts +3 -0
  65. package/src/constants/system_roles.test.ts +5 -0
  66. package/src/constants/system_roles.ts +3 -0
  67. package/src/constants/theme_foundations.test.ts +5 -0
  68. package/src/constants/theme_foundations.ts +6 -0
  69. package/src/constants/theme_status.test.ts +5 -0
  70. package/src/constants/theme_status.ts +5 -0
  71. package/src/constants/user_types.test.ts +5 -0
  72. package/src/constants/user_types.ts +17 -0
  73. package/src/constants/white_label_accessibilities.test.ts +5 -0
  74. package/src/constants/white_label_accessibilities.ts +6 -0
  75. package/src/cookie.test.ts +11 -0
  76. package/src/cookie.ts +16 -0
  77. package/src/entities/address.test.ts +7 -0
  78. package/src/entities/address.ts +65 -0
  79. package/src/entities/assignment.test.ts +39 -0
  80. package/src/entities/assignment.ts +92 -0
  81. package/src/entities/automatic_payment_relationship.test.ts +8 -0
  82. package/src/entities/automatic_payment_relationship.ts +26 -0
  83. package/src/entities/backup.test.ts +7 -0
  84. package/src/entities/backup.ts +14 -0
  85. package/src/entities/bank.test.ts +7 -0
  86. package/src/entities/bank.ts +45 -0
  87. package/src/entities/cart.test.ts +50 -0
  88. package/src/entities/cart.ts +89 -0
  89. package/src/entities/cart_item.test.ts +33 -0
  90. package/src/entities/cart_item.ts +77 -0
  91. package/src/entities/cart_shipment_group.ts +21 -0
  92. package/src/entities/cart_shipment_quote.ts +33 -0
  93. package/src/entities/category.test.ts +57 -0
  94. package/src/entities/category.ts +37 -0
  95. package/src/entities/company.test.ts +7 -0
  96. package/src/entities/company.ts +275 -0
  97. package/src/entities/company_invitation.test.ts +7 -0
  98. package/src/entities/company_invitation.ts +33 -0
  99. package/src/entities/component.test.ts +14 -0
  100. package/src/entities/component.ts +92 -0
  101. package/src/entities/component_tag.test.ts +7 -0
  102. package/src/entities/component_tag.ts +17 -0
  103. package/src/entities/component_version.test.ts +7 -0
  104. package/src/entities/component_version.ts +30 -0
  105. package/src/entities/country_tax.test.ts +13 -0
  106. package/src/entities/country_tax.ts +46 -0
  107. package/src/entities/discount.test.ts +18 -0
  108. package/src/entities/discount.ts +34 -0
  109. package/src/entities/discount_group.test.ts +7 -0
  110. package/src/entities/discount_group.ts +28 -0
  111. package/src/entities/domain.test.ts +79 -0
  112. package/src/entities/domain.ts +246 -0
  113. package/src/entities/domain_invitation.test.ts +7 -0
  114. package/src/entities/domain_invitation.ts +36 -0
  115. package/src/entities/domain_tag.test.ts +7 -0
  116. package/src/entities/domain_tag.ts +42 -0
  117. package/src/entities/draft.test.ts +43 -0
  118. package/src/entities/draft.ts +88 -0
  119. package/src/entities/draft_comment.test.ts +7 -0
  120. package/src/entities/draft_comment.ts +54 -0
  121. package/src/entities/draft_template.test.ts +7 -0
  122. package/src/entities/draft_template.ts +40 -0
  123. package/src/entities/email_address.test.ts +7 -0
  124. package/src/entities/email_address.ts +24 -0
  125. package/src/entities/email_counter.test.ts +7 -0
  126. package/src/entities/email_counter.ts +20 -0
  127. package/src/entities/enrolled_domain.test.ts +7 -0
  128. package/src/entities/enrolled_domain.ts +40 -0
  129. package/src/entities/exchange_rate.test.ts +7 -0
  130. package/src/entities/exchange_rate.ts +22 -0
  131. package/src/entities/file.test.ts +43 -0
  132. package/src/entities/file.ts +172 -0
  133. package/src/entities/internal_tag.test.ts +7 -0
  134. package/src/entities/internal_tag.ts +51 -0
  135. package/src/entities/inventory.test.ts +28 -0
  136. package/src/entities/inventory.ts +56 -0
  137. package/src/entities/inventory_unit_variation.test.ts +7 -0
  138. package/src/entities/inventory_unit_variation.ts +29 -0
  139. package/src/entities/invoice.test.ts +43 -0
  140. package/src/entities/invoice.ts +179 -0
  141. package/src/entities/item.test.ts +19 -0
  142. package/src/entities/item.ts +44 -0
  143. package/src/entities/job.test.ts +95 -0
  144. package/src/entities/job.ts +349 -0
  145. package/src/entities/job_comment.test.ts +7 -0
  146. package/src/entities/job_comment.ts +50 -0
  147. package/src/entities/matching_inventory.ts +29 -0
  148. package/src/entities/menu.test.ts +7 -0
  149. package/src/entities/menu.ts +30 -0
  150. package/src/entities/menu_item.test.ts +7 -0
  151. package/src/entities/menu_item.ts +29 -0
  152. package/src/entities/notification.test.ts +7 -0
  153. package/src/entities/notification.ts +99 -0
  154. package/src/entities/page.test.ts +7 -0
  155. package/src/entities/page.ts +32 -0
  156. package/src/entities/payment.test.ts +19 -0
  157. package/src/entities/payment.ts +65 -0
  158. package/src/entities/payment_device.test.ts +7 -0
  159. package/src/entities/payment_device.ts +30 -0
  160. package/src/entities/phone_number.test.ts +7 -0
  161. package/src/entities/phone_number.ts +36 -0
  162. package/src/entities/product.test.ts +750 -0
  163. package/src/entities/product.ts +427 -0
  164. package/src/entities/production_comment.test.ts +7 -0
  165. package/src/entities/production_comment.ts +50 -0
  166. package/src/entities/quote.test.ts +98 -0
  167. package/src/entities/quote.ts +140 -0
  168. package/src/entities/quote_item.test.ts +50 -0
  169. package/src/entities/quote_item.ts +71 -0
  170. package/src/entities/seo_domain_page.test.ts +7 -0
  171. package/src/entities/seo_domain_page.ts +33 -0
  172. package/src/entities/session.test.ts +7 -0
  173. package/src/entities/session.ts +28 -0
  174. package/src/entities/shipment.test.ts +24 -0
  175. package/src/entities/shipment.ts +185 -0
  176. package/src/entities/shipment_item.test.ts +7 -0
  177. package/src/entities/shipment_item.ts +15 -0
  178. package/src/entities/shipment_item_fulfillment.test.ts +8 -0
  179. package/src/entities/shipment_item_fulfillment.ts +15 -0
  180. package/src/entities/shipment_method.test.ts +7 -0
  181. package/src/entities/shipment_method.ts +61 -0
  182. package/src/entities/shipment_method_variation.test.ts +7 -0
  183. package/src/entities/shipment_method_variation.ts +39 -0
  184. package/src/entities/short_url.test.ts +7 -0
  185. package/src/entities/short_url.ts +36 -0
  186. package/src/entities/subscription_plan.test.ts +7 -0
  187. package/src/entities/subscription_plan.ts +73 -0
  188. package/src/entities/supply_domain.test.ts +7 -0
  189. package/src/entities/supply_domain.ts +27 -0
  190. package/src/entities/system_role.test.ts +7 -0
  191. package/src/entities/system_role.ts +17 -0
  192. package/src/entities/theme.test.ts +38 -0
  193. package/src/entities/theme.ts +267 -0
  194. package/src/entities/theme_css_setting.test.ts +7 -0
  195. package/src/entities/theme_css_setting.ts +23 -0
  196. package/src/entities/user.test.ts +154 -0
  197. package/src/entities/user.ts +394 -0
  198. package/src/entities/user_company.test.ts +7 -0
  199. package/src/entities/user_company.ts +27 -0
  200. package/src/entities/variation.test.ts +7 -0
  201. package/src/entities/variation.ts +61 -0
  202. package/src/entities/variation_field.test.ts +76 -0
  203. package/src/entities/variation_field.ts +169 -0
  204. package/src/entities/variation_fields_option.test.ts +46 -0
  205. package/src/entities/variation_fields_option.ts +95 -0
  206. package/src/entities/variation_option.test.ts +7 -0
  207. package/src/entities/variation_option.ts +52 -0
  208. package/src/entities/variations_group.test.ts +7 -0
  209. package/src/entities/variations_group.ts +41 -0
  210. package/src/entity.ts +1002 -0
  211. package/src/merchi.test.ts +25 -0
  212. package/src/merchi.ts +379 -0
  213. package/src/request.test.ts +43 -0
  214. package/src/request.ts +124 -0
  215. package/src/test_util.ts +18 -0
  216. package/src/toasts.test.ts +38 -0
  217. package/src/toasts.ts +34 -0
  218. package/src/util/float.ts +12 -0
  219. package/src/util/query_string.test.ts +7 -0
  220. package/src/util/query_string.ts +11 -0
  221. package/src/util/validation.test.ts +16 -0
  222. package/src/util/validation.ts +2 -0
  223. package/src/uuid.test.ts +15 -0
  224. package/src/uuid.ts +8 -0
  225. package/tsconfig.json +23 -0
  226. package/webpack.config.cjs +27 -0
package/src/entity.ts ADDED
@@ -0,0 +1,1002 @@
1
+ import 'reflect-metadata';
2
+ // eslint-disable-next-line no-unused-vars
3
+ import { RequestOptions } from './request';
4
+ // eslint-disable-next-line no-unused-vars
5
+ import { Merchi } from './merchi';
6
+ // eslint-disable-next-line no-unused-vars
7
+ import { NotificationType } from './constants/notification_types';
8
+ // eslint-disable-next-line no-unused-vars
9
+ import { NotificationSection } from './constants/notification_sections';
10
+ // eslint-disable-next-line no-unused-vars
11
+ import { Role } from './constants/roles';
12
+ // eslint-disable-next-line no-unused-vars
13
+ import { DomainType } from './constants/domain_types';
14
+ // eslint-disable-next-line no-unused-vars
15
+ import { ProductType } from './constants/product_types';
16
+ import { generateUUID } from './uuid';
17
+
18
+ function toUnixTimestamp(date: Date) {
19
+ return parseInt(String(date.getTime() / 1000)).toFixed(0);
20
+ }
21
+
22
+ interface Counter {
23
+ value: number;
24
+ }
25
+
26
+ interface PropertyOptions {
27
+ embeddedByDefault?: boolean;
28
+ jsonName?: string;
29
+ type?: any;
30
+ arrayType?: string;
31
+ }
32
+
33
+ export interface EmbedDescriptor {
34
+ [property: string]: {} | EmbedDescriptor;
35
+ }
36
+
37
+ export interface NestedIncludeArchivedDescriptor {
38
+ [property: string]: {} | boolean | NestedIncludeArchivedDescriptor;
39
+ }
40
+
41
+ export type IncludeArchivedDescriptor = boolean | NestedIncludeArchivedDescriptor;
42
+
43
+ interface FromJsonOptions {
44
+ arrayValueStrict?: boolean;
45
+ makeDirty?: boolean;
46
+ }
47
+
48
+ interface CreateOptions {
49
+ withRights?: boolean;
50
+ embed?: EmbedDescriptor;
51
+ }
52
+
53
+ interface SaveOptions {
54
+ withRights?: boolean;
55
+ embed?: EmbedDescriptor;
56
+ }
57
+
58
+ interface DeleteOptions {
59
+ withRights?: boolean;
60
+ }
61
+
62
+ interface GetOptions {
63
+ embed?: EmbedDescriptor;
64
+ includeArchived?: IncludeArchivedDescriptor;
65
+ withRights?: boolean;
66
+ }
67
+
68
+ export enum SerialiseMethod {
69
+ // eslint-disable-next-line no-unused-vars
70
+ TO_DICT = 'to_dict',
71
+ // eslint-disable-next-line no-unused-vars
72
+ ONLY_ID = 'only_id',
73
+ }
74
+
75
+ interface ListOptions {
76
+ as?: string;
77
+ asRole?: Role;
78
+ businessDomainsOnly?: boolean;
79
+ categoryId?: number;
80
+ clientId?: number;
81
+ clientCompanyId?: number;
82
+ clientOnly?: boolean;
83
+ companyCustomerId?: number;
84
+ companyId?: number;
85
+ companySupplierId?: number;
86
+ componentId?: number;
87
+ dateFrom?: Date;
88
+ dateTo?: Date;
89
+ domainRoles?: Role[];
90
+ domainTypes?: DomainType[];
91
+ doesNotHaveAdminDomain?: boolean;
92
+ embed?: EmbedDescriptor;
93
+ entityTypes?: number[];
94
+ exclude?: number[];
95
+ excludeComponents?: string[];
96
+ excludeDomains?: number[];
97
+ groupBuyForJobId?: number;
98
+ groupBuyOnly?: boolean;
99
+ inbound?: boolean;
100
+ includeOnly?: number[];
101
+ inDomain?: number;
102
+ inDomainName?: string;
103
+ inDomainRoles?: number[];
104
+ isMaster?: boolean;
105
+ isOrder?: boolean;
106
+ isPrivate?: boolean;
107
+ jobNotifiable?: number;
108
+ limit?: number;
109
+ managedDomainsOnly?: boolean;
110
+ managedOnly?: boolean;
111
+ managerId?: number;
112
+ masterProduct?: number;
113
+ memberOnly?: boolean;
114
+ merchiOnly?: boolean;
115
+ notificationJob?: number;
116
+ notificationRecipient?: number;
117
+ notificationType?: NotificationType;
118
+ offset?: number;
119
+ orClientId?: number;
120
+ orClientCompanyId?: number;
121
+ order?: string;
122
+ originalOf?: number;
123
+ platformCategoryId?: number;
124
+ productTypes?: ProductType[];
125
+ publicOnly?: boolean;
126
+ q?: string;
127
+ receiverId?: number;
128
+ relatedAssignment?: number;
129
+ relatedComponent?: number;
130
+ relatedDraft?: number;
131
+ relatedJob?: number;
132
+ relatedProduct?: number;
133
+ relatedUser?: number;
134
+ savedByUser?: number;
135
+ section?: NotificationSection;
136
+ senderRole?: Role;
137
+ serialiseMethod?: SerialiseMethod;
138
+ shopifyOnly?: boolean;
139
+ sort?: string;
140
+ state?: string;
141
+ supplierResellOnly?: boolean;
142
+ tab?: string;
143
+ tags?: number[];
144
+ tagsInternal?: number[];
145
+ tagNames?: string[];
146
+ teamOnly?: boolean;
147
+ withRights?: boolean;
148
+ }
149
+
150
+ export interface ListMetadata {
151
+ canCreate?: boolean;
152
+ available: number;
153
+ count: number;
154
+ limit: number;
155
+ offset: number;
156
+ }
157
+
158
+ export interface ListResponse<T> {
159
+ items: T[];
160
+ metadata: ListMetadata;
161
+ }
162
+
163
+ interface SerialiseOptions {
164
+ existing?: FormData;
165
+ excludeOld?: boolean;
166
+ _prefix?: string;
167
+ }
168
+
169
+ interface PropertyInfo {
170
+ property: string; // this is the json name, e.g. 'id'
171
+ attribute: string; // this is the js attribute name, e.g. '_id'
172
+ type: Function; // e.g. Number
173
+ arrayType?: Entity; // if type is an array, arrayType is the element type
174
+ dirty: boolean; // true if out of date with server
175
+ currentValue?: any; // type is actually `this.type | undefined`
176
+ updatingOrder: boolean;
177
+ embeddedByDefault: boolean;
178
+ }
179
+
180
+ export class Entity {
181
+ private static jsonNameKey = Symbol('jsonName');
182
+ private static arrayTypeKey = Symbol('arrayType');
183
+ private static extraOptionsKey = Symbol('extraOptions');
184
+ private static propertiesSetKey = Symbol('propertiesSet');
185
+
186
+ protected static resourceName: string;
187
+ protected static singularName: string;
188
+ protected static pluralName: string;
189
+ protected static primaryKey = 'id';
190
+
191
+ // these will be set by the parent Merchi object
192
+ public merchi!: Merchi;
193
+ public static merchi: Merchi;
194
+
195
+ public isDirty = false;
196
+ // maps json names like 'id' to information about that property
197
+ public propertiesMap: Map<string, PropertyInfo>;
198
+ public readonly backObjects: Set<Entity> = new Set();
199
+
200
+ public key = generateUUID();
201
+
202
+ protected isSingleEntityProperty(info: PropertyInfo) {
203
+ // the choice of 'Product' below is unimportant -- all Entities should
204
+ // have the same prototype but i don't know how to get instanceof
205
+ // working, so i just compare prototypes directly.
206
+ return (
207
+ info.type.prototype === this.merchi.Product.prototype ||
208
+ info.type.prototype instanceof Entity
209
+ );
210
+ }
211
+
212
+ protected static property(options?: PropertyOptions) {
213
+ return function (target: Entity, propertyKey: string) {
214
+ if (!options) {
215
+ options = {};
216
+ }
217
+ const arrayType = options.arrayType;
218
+ const jsonName = options.jsonName || propertyKey;
219
+ let properties = Reflect.getMetadata(Entity.propertiesSetKey,
220
+ target.constructor);
221
+ properties = properties || new Set();
222
+ properties.add(propertyKey);
223
+ Reflect.defineMetadata(Entity.propertiesSetKey, properties,
224
+ target.constructor);
225
+ Reflect.defineMetadata(Entity.jsonNameKey, jsonName, target,
226
+ propertyKey);
227
+ Reflect.defineMetadata(Entity.arrayTypeKey, arrayType, target,
228
+ propertyKey);
229
+ Reflect.defineMetadata(Entity.extraOptionsKey, options, target,
230
+ propertyKey);
231
+ };
232
+ }
233
+
234
+ protected makePropertiesMap() {
235
+ const map = new Map();
236
+ const self = this;
237
+ const properties = Reflect.getMetadata(Entity.propertiesSetKey,
238
+ this.constructor);
239
+ properties.forEach(function(attributeName: any) {
240
+ const jsonName = Reflect.getMetadata(Entity.jsonNameKey, self,
241
+ attributeName);
242
+ const propertyType = Reflect.getMetadata('design:type', self,
243
+ attributeName);
244
+ // the array type is needed because 'design:type' breaks down
245
+ // with recursive classes, and also, does not contain the type
246
+ // of an arrays elements, which we need
247
+ const arrayType = Reflect.getMetadata(Entity.arrayTypeKey, self,
248
+ attributeName);
249
+ const realArrayType = self.getEntityClass(arrayType);
250
+ const options: PropertyOptions =
251
+ Reflect.getMetadata(Entity.extraOptionsKey, self, attributeName);
252
+ if (arrayType !== undefined) {
253
+ /* istanbul ignore next */
254
+ if (propertyType !== Object && propertyType !== Array) {
255
+ /* istanbul ignore next */
256
+ throw new Error('array type can only be given for arrays');
257
+ }
258
+ }
259
+ let type;
260
+ if (options.type === undefined) {
261
+ type = propertyType;
262
+ } else {
263
+ type = options.type;
264
+ if (typeof type === 'string') {
265
+ type = self.getEntityClass(type);
266
+ }
267
+ if (type.prototype instanceof Entity) {
268
+ type = self.merchi.setupClass(type);
269
+ }
270
+ }
271
+ const normallyEmbeddedByDefault = !(realArrayType ||
272
+ type.prototype instanceof Entity);
273
+ const embeddedByDefault = options.embeddedByDefault !== undefined ?
274
+ options.embeddedByDefault : normallyEmbeddedByDefault;
275
+ /* istanbul ignore next */
276
+ if (type === Object) {
277
+ /* istanbul ignore next */
278
+ const resource = (self.constructor as typeof Entity).resourceName;
279
+ const err = 'Bad attribute type ' +
280
+ `${resource}.${attributeName}: ${type}`;
281
+ throw new Error(err);
282
+ }
283
+ const propertyInfo: PropertyInfo = {property: jsonName,
284
+ attribute: attributeName,
285
+ type: type,
286
+ arrayType: realArrayType,
287
+ embeddedByDefault: embeddedByDefault,
288
+ dirty: true,
289
+ updatingOrder: false};
290
+ map.set(jsonName, propertyInfo);
291
+ });
292
+ return map;
293
+ }
294
+
295
+ public constructor(merchi?: Merchi) {
296
+ /* istanbul ignore next */
297
+ if (merchi !== undefined) {
298
+ this.merchi = merchi;
299
+ }
300
+ this.propertiesMap = this.makePropertiesMap();
301
+ this.setupProperties();
302
+ }
303
+
304
+ public getPrimaryKeyValue = () => {
305
+ const name: string = (this.constructor as typeof Entity).primaryKey;
306
+ const info = this.propertiesMap.get(name);
307
+ /* istanbul ignore next */
308
+ if (info !== undefined) {
309
+ return info.currentValue;
310
+ }
311
+ /* istanbul ignore next */
312
+ return undefined;
313
+ };
314
+
315
+ private setupProperties = () => {
316
+ const properties: any = {};
317
+ const makeSetSingle = (info: PropertyInfo) => {
318
+ const get = () => {
319
+ return info.currentValue;
320
+ };
321
+ const set = (newValue?: Entity) => {
322
+ info.currentValue = newValue;
323
+ this.addBackObject(newValue);
324
+ this.markDirty(info.property, newValue);
325
+ };
326
+ return { get: get,
327
+ set: set};
328
+ };
329
+ const makeSetScalar = (info: PropertyInfo) => {
330
+ const get = () => {
331
+ return info.currentValue;
332
+ };
333
+ const set = (newValue: any) => {
334
+ info.currentValue = newValue;
335
+ this.markDirty(info.property, newValue);
336
+ };
337
+ return { get: get,
338
+ set: set};
339
+ };
340
+ const makeSetArray = (info: PropertyInfo) => {
341
+ const get = () => {
342
+ return info.currentValue;
343
+ };
344
+ const set = (newValue?: Entity[]) => {
345
+ info.currentValue = newValue;
346
+ this.addBackObjectList(newValue);
347
+ this.markDirty(info.property, newValue);
348
+ };
349
+ return { get: get,
350
+ set: set};
351
+ };
352
+ for (const info of this.propertiesMap.values()) {
353
+ if (info.type.prototype instanceof Entity) {
354
+ properties[info.attribute] = makeSetSingle(info);
355
+ } else if (info.arrayType) {
356
+ properties[info.attribute] = makeSetArray(info);
357
+ } else {
358
+ properties[info.attribute] = makeSetScalar(info);
359
+ }
360
+ }
361
+ Object.defineProperties(this, properties);
362
+ };
363
+
364
+ public static get<T extends typeof Entity>(this: T, key: number | string,
365
+ options?: GetOptions):
366
+ Promise<InstanceType<T>>{
367
+ const resource = `/${this.resourceName}/${String(key)}/`;
368
+ const fetchOptions: RequestOptions = {};
369
+ fetchOptions.query = [];
370
+ if (options && options.embed) {
371
+ fetchOptions.query.push(['embed', JSON.stringify(options.embed)]);
372
+ }
373
+ if (options && options.includeArchived) {
374
+ fetchOptions.query.push(['include_archived',
375
+ JSON.stringify(options.includeArchived)]);
376
+ }
377
+ if (!(options && options.withRights)) {
378
+ fetchOptions.query.push(['skip_rights', 'y']);
379
+ }
380
+ return this.merchi.authenticatedFetch(resource, fetchOptions).
381
+ then((data: any) => {
382
+ const result: InstanceType<T> = (new this()) as InstanceType<T>;
383
+ result.fromJson(data[this.singularName]);
384
+ return result;
385
+ });
386
+ }
387
+
388
+ public static list<T extends typeof Entity>(this: T, options?: ListOptions):
389
+ Promise<ListResponse<InstanceType<T>>> {
390
+ const resource = `/${this.resourceName}/`;
391
+ const fetchOptions: RequestOptions = {};
392
+ fetchOptions.query = [];
393
+ if (options) {
394
+ if (options.embed) {
395
+ fetchOptions.query.push(['embed', JSON.stringify(options.embed)]);
396
+ }
397
+ if (options.offset !== undefined) {
398
+ fetchOptions.query.push(['offset', options.offset.toString()]);
399
+ }
400
+ if (options.limit !== undefined) {
401
+ fetchOptions.query.push(['limit', options.limit.toString()]);
402
+ }
403
+ if (options.q !== undefined) {
404
+ fetchOptions.query.push(['q', options.q]);
405
+ }
406
+ if (options.sort !== undefined) {
407
+ fetchOptions.query.push(['sort', options.sort]);
408
+ }
409
+ if (options.order !== undefined) {
410
+ fetchOptions.query.push(['order', options.order]);
411
+ }
412
+ if (options.serialiseMethod !== undefined) {
413
+ fetchOptions.query.push(['serialise_method', options.serialiseMethod]);
414
+ }
415
+ if (options.tab !== undefined) {
416
+ fetchOptions.query.push(['tab', options.tab]);
417
+ }
418
+ if (options.as !== undefined) {
419
+ fetchOptions.query.push(['as', options.as]);
420
+ }
421
+ if (options.state !== undefined) {
422
+ fetchOptions.query.push(['state', options.state]);
423
+ }
424
+ if (options.categoryId !== undefined) {
425
+ fetchOptions.query.push(['category_id', options.categoryId.toString()]);
426
+ }
427
+ if (options.platformCategoryId !== undefined) {
428
+ fetchOptions.query.push(['platform_category_id', options.platformCategoryId.toString()]);
429
+ }
430
+ if (options.inDomain !== undefined) {
431
+ fetchOptions.query.push(['in_domain', options.inDomain.toString()]);
432
+ }
433
+ if (options.inDomainName !== undefined) {
434
+ fetchOptions.query.push(['in_domain_name', options.inDomainName.toString()]);
435
+ }
436
+ if (options.inDomainRoles !== undefined) {
437
+ fetchOptions.query.push(['in_domain_roles',
438
+ JSON.stringify(options.inDomainRoles)]);
439
+ }
440
+ if (options.isPrivate !== undefined) {
441
+ fetchOptions.query.push(['is_private', options.isPrivate.toString()]);
442
+ }
443
+ if (options.asRole !== undefined) {
444
+ fetchOptions.query.push(['as_role', options.asRole.toString()]);
445
+ }
446
+ if (options.groupBuyOnly !== undefined) {
447
+ fetchOptions.query.push(
448
+ ['group_buy_only', options.groupBuyOnly.toString()]);
449
+ }
450
+ if (options.publicOnly !== undefined) {
451
+ fetchOptions.query.push(['public_only', options.publicOnly.toString()]);
452
+ }
453
+ if (options.managedOnly !== undefined) {
454
+ fetchOptions.query.push(['managed_only',
455
+ options.managedOnly.toString()]);
456
+ }
457
+ if (options.doesNotHaveAdminDomain !== undefined) {
458
+ fetchOptions.query.push(['does_not_have_admin_domain',
459
+ options.doesNotHaveAdminDomain.toString()]);
460
+ }
461
+ if (options.clientOnly !== undefined) {
462
+ fetchOptions.query.push(['client_only',
463
+ options.clientOnly.toString()]);
464
+ }
465
+ if (options.teamOnly !== undefined) {
466
+ fetchOptions.query.push(['team_only',
467
+ options.teamOnly.toString()]);
468
+ }
469
+ if (options.memberOnly !== undefined) {
470
+ fetchOptions.query.push(['member_only', options.memberOnly.toString()]);
471
+ }
472
+ if (options.merchiOnly !== undefined) {
473
+ fetchOptions.query.push(['merchi_only', options.merchiOnly.toString()]);
474
+ }
475
+ if (options.supplierResellOnly !== undefined) {
476
+ fetchOptions.query.push(
477
+ ['supplier_resell_only', options.supplierResellOnly.toString()]
478
+ );
479
+ }
480
+ if (options.shopifyOnly !== undefined) {
481
+ fetchOptions.query.push(['shopify_only', options.shopifyOnly.toString()]);
482
+ }
483
+ if (options.inbound !== undefined) {
484
+ fetchOptions.query.push(['inbound', options.inbound.toString()]);
485
+ }
486
+ if (options.isMaster !== undefined) {
487
+ fetchOptions.query.push(['is_master', options.isMaster.toString()]);
488
+ }
489
+ if (options.domainRoles !== undefined) {
490
+ fetchOptions.query.push(['domain_roles',
491
+ options.domainRoles.join(',')]);
492
+ }
493
+ if (options.domainTypes !== undefined) {
494
+ fetchOptions.query.push(['domain_types',
495
+ options.domainTypes.join(',')]);
496
+ }
497
+ if (options.entityTypes !== undefined) {
498
+ fetchOptions.query.push(['entity_types',
499
+ options.entityTypes.join(',')]);
500
+ }
501
+ if (options.productTypes !== undefined) {
502
+ fetchOptions.query.push(['product_types',
503
+ options.productTypes.join(',')]);
504
+ }
505
+ if (options.managedDomainsOnly !== undefined) {
506
+ fetchOptions.query.push(['managed_domains_only',
507
+ options.managedDomainsOnly.toString()]);
508
+ }
509
+ if (options.businessDomainsOnly !== undefined) {
510
+ fetchOptions.query.push(['business_domains_only',
511
+ options.businessDomainsOnly.toString()]);
512
+ }
513
+ if (options.dateFrom !== undefined) {
514
+ fetchOptions.query.push(['date_from',
515
+ toUnixTimestamp(options.dateFrom)]);
516
+ }
517
+ if (options.dateTo !== undefined) {
518
+ fetchOptions.query.push(['date_to', toUnixTimestamp(options.dateTo)]);
519
+ }
520
+ if (options.originalOf !== undefined) {
521
+ fetchOptions.query.push(['original_of',
522
+ options.originalOf.toString()]);
523
+ }
524
+ if (options.relatedComponent !== undefined) {
525
+ fetchOptions.query.push(['related_component',
526
+ options.relatedComponent.toString()]);
527
+ }
528
+ if (options.relatedAssignment !== undefined) {
529
+ fetchOptions.query.push(['related_assignment',
530
+ options.relatedAssignment.toString()]);
531
+ }
532
+ if (options.relatedDraft !== undefined) {
533
+ fetchOptions.query.push(['related_draft',
534
+ options.relatedDraft.toString()]);
535
+ }
536
+ if (options.relatedJob !== undefined) {
537
+ fetchOptions.query.push(['related_job', options.relatedJob.toString()]);
538
+ }
539
+ if (options.relatedProduct !== undefined) {
540
+ fetchOptions.query.push(['related_product',
541
+ options.relatedProduct.toString()]);
542
+ }
543
+ if (options.jobNotifiable !== undefined) {
544
+ fetchOptions.query.push(['job_notifiable',
545
+ options.jobNotifiable.toString()]);
546
+ }
547
+ if (options.notificationType !== undefined) {
548
+ fetchOptions.query.push(['notification_type',
549
+ options.notificationType.toString()]);
550
+ }
551
+ if (options.notificationRecipient !== undefined) {
552
+ fetchOptions.query.push(['notification_recipient',
553
+ options.notificationRecipient.toString()]);
554
+ }
555
+ if (options.notificationJob !== undefined) {
556
+ fetchOptions.query.push(['notification_job',
557
+ options.notificationJob.toString()]);
558
+ }
559
+ if (options.relatedUser !== undefined) {
560
+ fetchOptions.query.push(['related_user',
561
+ options.relatedUser.toString()]);
562
+ }
563
+ if (options.clientId !== undefined) {
564
+ fetchOptions.query.push(['client_id', options.clientId.toString()]);
565
+ }
566
+ if (options.managerId !== undefined) {
567
+ fetchOptions.query.push(['manager_id', options.managerId.toString()]);
568
+ }
569
+ if (options.masterProduct !== undefined) {
570
+ fetchOptions.query.push(['master_product', options.masterProduct.toString()]);
571
+ }
572
+ if (options.clientCompanyId !== undefined) {
573
+ fetchOptions.query.push(['client_company_id',
574
+ options.clientCompanyId.toString()]);
575
+ }
576
+ if (options.savedByUser !== undefined) {
577
+ fetchOptions.query.push(['saved_by_user',
578
+ options.savedByUser.toString()]);
579
+ }
580
+ if (options.receiverId !== undefined) {
581
+ fetchOptions.query.push(['receiver_id', options.receiverId.toString()]);
582
+ }
583
+ if (options.companyCustomerId !== undefined) {
584
+ fetchOptions.query.push(['company_customer_id', options.companyCustomerId.toString()]);
585
+ }
586
+ if (options.companyId !== undefined) {
587
+ fetchOptions.query.push(['company_id', options.companyId.toString()]);
588
+ }
589
+ if (options.companySupplierId !== undefined) {
590
+ fetchOptions.query.push(['company_supplier_id', options.companySupplierId.toString()]);
591
+ }
592
+ if (options.componentId !== undefined) {
593
+ fetchOptions.query.push(['component_id',
594
+ options.componentId.toString()]);
595
+ }
596
+ if (options.groupBuyForJobId !== undefined) {
597
+ fetchOptions.query.push(['group_buy_for_job_id',
598
+ options.groupBuyForJobId.toString()]);
599
+ }
600
+ if (options.section !== undefined) {
601
+ fetchOptions.query.push(['section', options.section.toString()]);
602
+ }
603
+ if (options.senderRole !== undefined) {
604
+ fetchOptions.query.push(['sender_role', options.senderRole.toString()]);
605
+ }
606
+ if (options.isOrder) {
607
+ fetchOptions.query.push(['is_order', 'true']);
608
+ }
609
+ if (options.tags !== undefined) {
610
+ fetchOptions.query.push(['tags', options.tags.join(',')]);
611
+ }
612
+ if (options.tagsInternal !== undefined) {
613
+ fetchOptions.query.push(['tags_internal', options.tagsInternal.join(',')]);
614
+ }
615
+ if (options.tagNames !== undefined) {
616
+ fetchOptions.query.push(['tag_names', options.tagNames.join(',')]);
617
+ }
618
+ if (options.exclude !== undefined) {
619
+ fetchOptions.query.push(['exclude', options.exclude.join(',')]);
620
+ }
621
+ if (options.excludeComponents !== undefined) {
622
+ fetchOptions.query.push(
623
+ ['exclude_components', options.excludeComponents.join(',')]);
624
+ }
625
+ if (options.excludeDomains !== undefined) {
626
+ fetchOptions.query.push(
627
+ ['exclude_domains', options.excludeDomains.join(',')]);
628
+ }
629
+ if (options.includeOnly !== undefined) {
630
+ fetchOptions.query.push(['include_only', options.includeOnly.join(',')]);
631
+ }
632
+ if (options.orClientId !== undefined) {
633
+ fetchOptions.query.push(['or_client_id', options.orClientId.toString()]);
634
+ }
635
+ if (options.orClientCompanyId !== undefined) {
636
+ fetchOptions.query.push(
637
+ ['or_client_company_id', options.orClientCompanyId.toString()]);
638
+ }
639
+ }
640
+ if (!(options && options.withRights)) {
641
+ fetchOptions.query.push(['skip_rights', 'y']);
642
+ }
643
+ return this.merchi.authenticatedFetch(resource, fetchOptions).then((data: any) => {
644
+ const metadata = {canCreate: data.canCreate,
645
+ available: data.available,
646
+ count: data.count,
647
+ limit: data.limit,
648
+ offset: data.offset};
649
+ const pluralName = this.pluralName;
650
+ const singularName = this.singularName;
651
+ const items: any[] = data[pluralName];
652
+ const entities = [];
653
+ for (const item of items) {
654
+ const entity: InstanceType<T> = (new this()) as InstanceType<T>;
655
+ entity.fromJson(item[singularName]);
656
+ entities.push(entity);
657
+ }
658
+ return {items: entities,
659
+ metadata: metadata};
660
+ });
661
+ }
662
+
663
+ public save = (options?: SaveOptions) => {
664
+ const primaryKey: number | string = this.getPrimaryKeyValue();
665
+ const constructor = this.constructor as typeof Entity;
666
+ const resourceName: string = constructor.resourceName;
667
+ const singularName: string = constructor.singularName;
668
+ const resource = `/${resourceName}/${String(primaryKey)}/`;
669
+ const data = this.toFormData();
670
+ const fetchOptions: RequestOptions = {method: 'PATCH',
671
+ body: data};
672
+ fetchOptions.query = [];
673
+ if (options && options.embed) {
674
+ fetchOptions.query.push(['embed', JSON.stringify(options.embed)]);
675
+ }
676
+ if (!(options && options.withRights)) {
677
+ fetchOptions.query.push(['skip_rights', 'y']);
678
+ }
679
+ return this.merchi.authenticatedFetch(resource, fetchOptions).then((data: any) => {
680
+ this.fromJson(data[singularName]);
681
+ this.cleanDirty();
682
+ return this;
683
+ });
684
+ };
685
+
686
+ public createFactory = (
687
+ {resourceName = (this.constructor as typeof Entity).resourceName}
688
+ ) => (options?: CreateOptions) => {
689
+ const resource = `/${resourceName}/`;
690
+ const data = this.toFormData();
691
+ const singularName = (this.constructor as typeof Entity).singularName;
692
+ const fetchOptions: RequestOptions = {method: 'POST',
693
+ body: data};
694
+
695
+ fetchOptions.query = [];
696
+ if (options && options.embed) {
697
+ fetchOptions.query.push(['embed', JSON.stringify(options.embed)]);
698
+ }
699
+ if (!(options && options.withRights)) {
700
+ fetchOptions.query.push(['skip_rights', 'y']);
701
+ }
702
+ return this.merchi.authenticatedFetch(resource, fetchOptions).
703
+ then((data: any) => {
704
+ this.fromJson(data[singularName]);
705
+ this.cleanDirty();
706
+ return this;});
707
+ };
708
+
709
+ public create = this.createFactory({});
710
+
711
+ private getEntityClass = (name: string) => {
712
+ if (name === undefined) {
713
+ return undefined;
714
+ }
715
+ return (this.merchi as any)[name];
716
+ };
717
+
718
+ public cleanDirty = () => {
719
+ // remove all dirty records of this entity, makes it untouched
720
+ // entity
721
+ this.isDirty = false;
722
+ for (const entry of this.propertiesMap.entries()) {
723
+ const propertyInfo = entry[1];
724
+ propertyInfo.dirty = false;
725
+ if (propertyInfo.currentValue !== undefined) {
726
+ if (propertyInfo.arrayType) {
727
+ propertyInfo.currentValue.map((v: any) => v.cleanDirty());
728
+ } else if (this.isSingleEntityProperty(propertyInfo)) {
729
+ if (propertyInfo.currentValue) {
730
+ propertyInfo.currentValue.cleanDirty();
731
+ }
732
+ }
733
+ }
734
+ }
735
+ };
736
+
737
+
738
+ public fromJson = (json: any, options?: FromJsonOptions) => {
739
+ options = options || {};
740
+ const { makeDirty = false, arrayValueStrict = true } = options;
741
+ options = { makeDirty, arrayValueStrict };
742
+ for (const key in json) {
743
+ const value: any = (json as any)[key];
744
+ if (value === undefined) continue;
745
+ const propertyInfo = this.propertiesMap.get(key);
746
+ if (propertyInfo !== undefined) {
747
+ propertyInfo.dirty = makeDirty;
748
+ if (propertyInfo.arrayType) {
749
+ // ignore array value if it is not an array and we did expect it
750
+ if (!arrayValueStrict && !Array.isArray(value)) continue;
751
+ const newValue: any = value.map((item: any, index: number) => {
752
+ const currentValue: any = propertyInfo.currentValue;
753
+ // if property already have an array of entities as relationship,
754
+ // try to merge with json one by one, this behavior may need to be
755
+ // configurable in the future.
756
+ if (currentValue && currentValue[index]) {
757
+ return currentValue[index].fromJson(item, options);
758
+ }
759
+ const nested = new (propertyInfo.arrayType as any)(this.merchi);
760
+ return nested.fromJson(item, options);
761
+ });
762
+ propertyInfo.currentValue = newValue;
763
+ } else if (this.isSingleEntityProperty(propertyInfo)) {
764
+ // if property already have a entity as relationship, try to merge
765
+ // with json first, this behavior may need to be configurable in the
766
+ // future.
767
+ if (propertyInfo.currentValue) {
768
+ propertyInfo.currentValue.fromJson(value, options);
769
+ } else {
770
+ const nested = new (propertyInfo.type as any)(this.merchi);
771
+ propertyInfo.currentValue = nested.fromJson(value, options);
772
+ }
773
+ } else {
774
+ propertyInfo.currentValue = value;
775
+ if (propertyInfo.type === Date && !!value) {
776
+ propertyInfo.currentValue = new Date(value * 1000);
777
+ }
778
+ }
779
+ }
780
+ }
781
+ return this;
782
+ };
783
+
784
+ public toJson = () => {
785
+ const json: any = {};
786
+ for (const entry of this.propertiesMap.entries()) {
787
+ const propertyName = entry[0];
788
+ const propertyInfo = entry[1];
789
+ if (propertyInfo.currentValue !== undefined) {
790
+ if (propertyInfo.arrayType) {
791
+ const array = propertyInfo.currentValue.map((v: any) => v.toJson());
792
+ json[propertyName] = array;
793
+ } else if (this.isSingleEntityProperty(propertyInfo)) {
794
+ if (propertyInfo.currentValue === null) {
795
+ json[propertyName] = null;
796
+ } else {
797
+ json[propertyName] = propertyInfo.currentValue.toJson();
798
+ }
799
+ } else {
800
+ const value = propertyInfo.currentValue;
801
+ json[propertyName] = value;
802
+ if (propertyInfo.type === Date && !!value) {
803
+ json[propertyName] = value.getTime() / 1000;
804
+ }
805
+ }
806
+ }
807
+ }
808
+ return json;
809
+ };
810
+
811
+ protected forEachProperty = (fn: (i: PropertyInfo) => void) => {
812
+ for (const info of this.propertiesMap.values()) {
813
+ fn(info);
814
+ }
815
+ };
816
+
817
+ public toFormData = (options?: SerialiseOptions,
818
+ fileIndex?: Counter): FormData => {
819
+ if (options === undefined) {
820
+ options = {};
821
+ }
822
+ const result = options.existing || new FormData();
823
+ const prefix = options._prefix || '';
824
+ const excludeOld = options.excludeOld === undefined ?
825
+ true : options.excludeOld;
826
+ if (fileIndex === undefined) {
827
+ fileIndex = {value: 0};
828
+ }
829
+ const appendData = (name: string, value: any) => {
830
+ /* istanbul ignore next */
831
+ if (name === undefined || value === undefined) {
832
+ /* istanbul ignore next */
833
+ return;
834
+ }
835
+ if (prefix) {
836
+ name = prefix + '-' + name;
837
+ }
838
+ result.set(name, value);
839
+ };
840
+ if ((this as any).fileData !== undefined) {
841
+ result.set(String(fileIndex.value), (this as any).fileData);
842
+ appendData('fileDataIndex', fileIndex.value);
843
+ fileIndex.value++;
844
+ }
845
+ const processArrayProperty = (info: PropertyInfo, value: Entity[]) => {
846
+ const remoteCount = value.length;
847
+ const initialLength = Array.from((result as any).entries()).length;
848
+ if (remoteCount === 0 && info.dirty) {
849
+ appendData(info.property + '-count', 0);
850
+ return;
851
+ }
852
+
853
+ let isDirty = false;
854
+ for (const item of value) {
855
+ if (item.isDirty) {
856
+ isDirty = true;
857
+ break;
858
+ }
859
+ }
860
+ isDirty = isDirty || info.dirty;
861
+ if (!isDirty && excludeOld) {
862
+ return;
863
+ }
864
+ for (let i = 0; i < remoteCount; ++i) {
865
+ let innerPrefix = info.property + '-' + i;
866
+ if (prefix) {
867
+ innerPrefix = prefix + '-' + innerPrefix;
868
+ }
869
+ value[i].toFormData(
870
+ {existing: result, _prefix: innerPrefix, excludeOld: excludeOld},
871
+ fileIndex);
872
+ }
873
+ const finalLength = Array.from((result as any).entries()).length;
874
+ if ((finalLength - initialLength) > 0) {
875
+ appendData(info.property + '-count', remoteCount);
876
+ if (info.updatingOrder) {
877
+ appendData(info.property + '-*updateOrder', 'true');
878
+ }
879
+ }
880
+ };
881
+ const processSingleEntityProperty = (info: PropertyInfo, value: Entity) => {
882
+ const primaryKey: string = (this.constructor as typeof Entity).primaryKey;
883
+ let innerPrefix = info.property + '-0';
884
+ if (prefix) {
885
+ innerPrefix = prefix + '-' + innerPrefix;
886
+ }
887
+ if (value === null) {
888
+ if (info.dirty || !excludeOld) {
889
+ appendData(info.property + '-0-' + primaryKey, '-1');
890
+ appendData(info.property + '-count', 1);
891
+ }
892
+ return;
893
+ }
894
+ if (!(info.dirty || (value.isDirty)) && excludeOld) {
895
+ return;
896
+ }
897
+ const initialLength = Array.from((result as any).entries()).length;
898
+ value.toFormData(
899
+ {existing: result, _prefix: innerPrefix, excludeOld: excludeOld},
900
+ fileIndex);
901
+ const finalLength = Array.from((result as any).entries()).length;
902
+ if ((finalLength - initialLength) > 0) {
903
+ appendData(info.property + '-count', 1);
904
+ }
905
+ };
906
+ const processScalarProperty = (info: PropertyInfo, value: any) => {
907
+ const primaryKey: string = (this.constructor as typeof Entity).primaryKey;
908
+ if (!excludeOld ||
909
+ info.dirty ||
910
+ (info.property === primaryKey && value)) {
911
+ if (info.type === Date && !!value) {
912
+ value = value.getTime() / 1000;
913
+ }
914
+ appendData(info.property, value);
915
+ }
916
+ };
917
+ const processProperty = (info: PropertyInfo) => {
918
+ const value = (this as any)[info.attribute];
919
+ if (value === undefined) {
920
+ return;
921
+ } else if (info.arrayType) {
922
+ processArrayProperty(info, value);
923
+ } else if (this.isSingleEntityProperty(info)) {
924
+ processSingleEntityProperty(info, value);
925
+ } else {
926
+ processScalarProperty(info, value);
927
+ }
928
+ };
929
+ this.forEachProperty(processProperty);
930
+ return result;
931
+ };
932
+
933
+ protected addBackObject = (remote?: Entity) => {
934
+ if (remote !== undefined) {
935
+ remote.backObjects.add(this);
936
+ }
937
+ };
938
+
939
+ protected addBackObjectList = (remotes?: Entity[]) => {
940
+ if (remotes !== undefined) {
941
+ remotes.forEach(this.addBackObject);
942
+ }
943
+ };
944
+
945
+ public updateOrder = (property: string) => {
946
+ (this.propertiesMap.get(property) as PropertyInfo).updatingOrder = true;
947
+ };
948
+
949
+ protected markDirty = (property: string, newValue: any) => {
950
+ if (newValue === undefined) {
951
+ return;
952
+ }
953
+ (this.propertiesMap.get(property) as PropertyInfo).dirty = true;
954
+ const openSet: Entity[] = []; /* Queue (BFS) */
955
+ const closedSet = new Set();
956
+ openSet.push(this);
957
+ while (openSet.length > 0) {
958
+ const current = openSet.shift() as Entity;
959
+ if (!closedSet.has(current)) {
960
+ current.isDirty = true;
961
+ closedSet.add(current);
962
+ for (const child of current.backObjects) openSet.push(child);
963
+ }
964
+ }
965
+ };
966
+
967
+ public delete = (options?: DeleteOptions) => {
968
+ const primaryKey: number = this.getPrimaryKeyValue();
969
+ const resourceName: string = (this.constructor as any).resourceName;
970
+ const resource = `/${resourceName}/${String(primaryKey)}/`;
971
+ const fetchOptions: RequestOptions = {method: 'DELETE'};
972
+ fetchOptions.query = [];
973
+ if (!(options && options.withRights)) {
974
+ fetchOptions.query.push(['skip_rights', 'y']);
975
+ }
976
+ if (this.merchi.sessionToken) {
977
+ fetchOptions.query.push(['session_token', this.merchi.sessionToken]);
978
+ }
979
+ return this.merchi.authenticatedFetch(resource, fetchOptions, true).then(
980
+ () => {return this;});
981
+ };
982
+
983
+ public recover = (options?: SaveOptions) => {
984
+ const primaryKey: number | string = this.getPrimaryKeyValue();
985
+ const constructor = this.constructor as typeof Entity;
986
+ const resourceName: string = constructor.resourceName;
987
+ const singularName: string = constructor.singularName;
988
+ const resource = `/unarchive/${resourceName}/${String(primaryKey)}/`;
989
+ const fetchOptions: RequestOptions = {method: 'POST'};
990
+ fetchOptions.query = [];
991
+ if (options && options.embed) {
992
+ fetchOptions.query.push(['embed', JSON.stringify(options.embed)]);
993
+ }
994
+ if (!(options && options.withRights)) {
995
+ fetchOptions.query.push(['skip_rights', 'y']);
996
+ }
997
+ return this.merchi.authenticatedFetch(resource, fetchOptions).then((data: any) => {
998
+ this.fromJson(data[singularName]);
999
+ return this;
1000
+ });
1001
+ };
1002
+ }