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.
- package/.babelrc +13 -0
- package/.eslintrc.cjs +43 -0
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/jest.config.js +17 -0
- package/package.json +44 -0
- package/src/constants/auto_assign_production_on_actions.ts +6 -0
- package/src/constants/call_to_actions.test.ts +5 -0
- package/src/constants/call_to_actions.ts +14 -0
- package/src/constants/discount_types.test.ts +5 -0
- package/src/constants/discount_types.ts +3 -0
- package/src/constants/domain_types.test.ts +5 -0
- package/src/constants/domain_types.ts +12 -0
- package/src/constants/errors.ts +32 -0
- package/src/constants/event_types.test.ts +5 -0
- package/src/constants/event_types.ts +5 -0
- package/src/constants/field_types.test.ts +5 -0
- package/src/constants/field_types.ts +13 -0
- package/src/constants/input_types.test.ts +5 -0
- package/src/constants/input_types.ts +13 -0
- package/src/constants/inventory_status.test.ts +5 -0
- package/src/constants/inventory_statuses.ts +6 -0
- package/src/constants/invoice_types.test.ts +5 -0
- package/src/constants/invoice_types.ts +5 -0
- package/src/constants/item_types.test.ts +5 -0
- package/src/constants/item_types.ts +5 -0
- package/src/constants/job/drafting_status.test.ts +5 -0
- package/src/constants/job/drafting_status.ts +7 -0
- package/src/constants/job/payment_status.test.ts +5 -0
- package/src/constants/job/payment_status.ts +7 -0
- package/src/constants/job/production_status.test.ts +5 -0
- package/src/constants/job/production_status.ts +12 -0
- package/src/constants/job/shipment_status.test.ts +5 -0
- package/src/constants/job/shipment_status.ts +11 -0
- package/src/constants/job/supply_chain_request_status.test.ts +5 -0
- package/src/constants/job/supply_chain_request_status.ts +6 -0
- package/src/constants/job_priorities.test.ts +5 -0
- package/src/constants/job_priorities.ts +6 -0
- package/src/constants/job_status.test.ts +5 -0
- package/src/constants/job_status.ts +49 -0
- package/src/constants/job_types.test.ts +5 -0
- package/src/constants/job_types.ts +15 -0
- package/src/constants/notification_sections.test.ts +5 -0
- package/src/constants/notification_sections.ts +13 -0
- package/src/constants/notification_types.test.ts +5 -0
- package/src/constants/notification_types.ts +97 -0
- package/src/constants/notification_urgencies.test.ts +5 -0
- package/src/constants/notification_urgencies.ts +7 -0
- package/src/constants/payment_types.test.ts +5 -0
- package/src/constants/payment_types.ts +10 -0
- package/src/constants/platform.test.ts +15 -0
- package/src/constants/platform.ts +6 -0
- package/src/constants/product_types.test.ts +6 -0
- package/src/constants/product_types.ts +25 -0
- package/src/constants/rights.test.ts +5 -0
- package/src/constants/rights.ts +5 -0
- package/src/constants/robots_meta_directives.test.ts +5 -0
- package/src/constants/robots_meta_directives.ts +11 -0
- package/src/constants/roles.test.ts +5 -0
- package/src/constants/roles.ts +38 -0
- package/src/constants/shipment_companies.test.ts +5 -0
- package/src/constants/shipment_companies.ts +18 -0
- package/src/constants/shipment_services.test.ts +5 -0
- package/src/constants/shipment_services.ts +3 -0
- package/src/constants/system_roles.test.ts +5 -0
- package/src/constants/system_roles.ts +3 -0
- package/src/constants/theme_foundations.test.ts +5 -0
- package/src/constants/theme_foundations.ts +6 -0
- package/src/constants/theme_status.test.ts +5 -0
- package/src/constants/theme_status.ts +5 -0
- package/src/constants/user_types.test.ts +5 -0
- package/src/constants/user_types.ts +17 -0
- package/src/constants/white_label_accessibilities.test.ts +5 -0
- package/src/constants/white_label_accessibilities.ts +6 -0
- package/src/cookie.test.ts +11 -0
- package/src/cookie.ts +16 -0
- package/src/entities/address.test.ts +7 -0
- package/src/entities/address.ts +65 -0
- package/src/entities/assignment.test.ts +39 -0
- package/src/entities/assignment.ts +92 -0
- package/src/entities/automatic_payment_relationship.test.ts +8 -0
- package/src/entities/automatic_payment_relationship.ts +26 -0
- package/src/entities/backup.test.ts +7 -0
- package/src/entities/backup.ts +14 -0
- package/src/entities/bank.test.ts +7 -0
- package/src/entities/bank.ts +45 -0
- package/src/entities/cart.test.ts +50 -0
- package/src/entities/cart.ts +89 -0
- package/src/entities/cart_item.test.ts +33 -0
- package/src/entities/cart_item.ts +77 -0
- package/src/entities/cart_shipment_group.ts +21 -0
- package/src/entities/cart_shipment_quote.ts +33 -0
- package/src/entities/category.test.ts +57 -0
- package/src/entities/category.ts +37 -0
- package/src/entities/company.test.ts +7 -0
- package/src/entities/company.ts +275 -0
- package/src/entities/company_invitation.test.ts +7 -0
- package/src/entities/company_invitation.ts +33 -0
- package/src/entities/component.test.ts +14 -0
- package/src/entities/component.ts +92 -0
- package/src/entities/component_tag.test.ts +7 -0
- package/src/entities/component_tag.ts +17 -0
- package/src/entities/component_version.test.ts +7 -0
- package/src/entities/component_version.ts +30 -0
- package/src/entities/country_tax.test.ts +13 -0
- package/src/entities/country_tax.ts +46 -0
- package/src/entities/discount.test.ts +18 -0
- package/src/entities/discount.ts +34 -0
- package/src/entities/discount_group.test.ts +7 -0
- package/src/entities/discount_group.ts +28 -0
- package/src/entities/domain.test.ts +79 -0
- package/src/entities/domain.ts +246 -0
- package/src/entities/domain_invitation.test.ts +7 -0
- package/src/entities/domain_invitation.ts +36 -0
- package/src/entities/domain_tag.test.ts +7 -0
- package/src/entities/domain_tag.ts +42 -0
- package/src/entities/draft.test.ts +43 -0
- package/src/entities/draft.ts +88 -0
- package/src/entities/draft_comment.test.ts +7 -0
- package/src/entities/draft_comment.ts +54 -0
- package/src/entities/draft_template.test.ts +7 -0
- package/src/entities/draft_template.ts +40 -0
- package/src/entities/email_address.test.ts +7 -0
- package/src/entities/email_address.ts +24 -0
- package/src/entities/email_counter.test.ts +7 -0
- package/src/entities/email_counter.ts +20 -0
- package/src/entities/enrolled_domain.test.ts +7 -0
- package/src/entities/enrolled_domain.ts +40 -0
- package/src/entities/exchange_rate.test.ts +7 -0
- package/src/entities/exchange_rate.ts +22 -0
- package/src/entities/file.test.ts +43 -0
- package/src/entities/file.ts +172 -0
- package/src/entities/internal_tag.test.ts +7 -0
- package/src/entities/internal_tag.ts +51 -0
- package/src/entities/inventory.test.ts +28 -0
- package/src/entities/inventory.ts +56 -0
- package/src/entities/inventory_unit_variation.test.ts +7 -0
- package/src/entities/inventory_unit_variation.ts +29 -0
- package/src/entities/invoice.test.ts +43 -0
- package/src/entities/invoice.ts +179 -0
- package/src/entities/item.test.ts +19 -0
- package/src/entities/item.ts +44 -0
- package/src/entities/job.test.ts +95 -0
- package/src/entities/job.ts +349 -0
- package/src/entities/job_comment.test.ts +7 -0
- package/src/entities/job_comment.ts +50 -0
- package/src/entities/matching_inventory.ts +29 -0
- package/src/entities/menu.test.ts +7 -0
- package/src/entities/menu.ts +30 -0
- package/src/entities/menu_item.test.ts +7 -0
- package/src/entities/menu_item.ts +29 -0
- package/src/entities/notification.test.ts +7 -0
- package/src/entities/notification.ts +99 -0
- package/src/entities/page.test.ts +7 -0
- package/src/entities/page.ts +32 -0
- package/src/entities/payment.test.ts +19 -0
- package/src/entities/payment.ts +65 -0
- package/src/entities/payment_device.test.ts +7 -0
- package/src/entities/payment_device.ts +30 -0
- package/src/entities/phone_number.test.ts +7 -0
- package/src/entities/phone_number.ts +36 -0
- package/src/entities/product.test.ts +750 -0
- package/src/entities/product.ts +427 -0
- package/src/entities/production_comment.test.ts +7 -0
- package/src/entities/production_comment.ts +50 -0
- package/src/entities/quote.test.ts +98 -0
- package/src/entities/quote.ts +140 -0
- package/src/entities/quote_item.test.ts +50 -0
- package/src/entities/quote_item.ts +71 -0
- package/src/entities/seo_domain_page.test.ts +7 -0
- package/src/entities/seo_domain_page.ts +33 -0
- package/src/entities/session.test.ts +7 -0
- package/src/entities/session.ts +28 -0
- package/src/entities/shipment.test.ts +24 -0
- package/src/entities/shipment.ts +185 -0
- package/src/entities/shipment_item.test.ts +7 -0
- package/src/entities/shipment_item.ts +15 -0
- package/src/entities/shipment_item_fulfillment.test.ts +8 -0
- package/src/entities/shipment_item_fulfillment.ts +15 -0
- package/src/entities/shipment_method.test.ts +7 -0
- package/src/entities/shipment_method.ts +61 -0
- package/src/entities/shipment_method_variation.test.ts +7 -0
- package/src/entities/shipment_method_variation.ts +39 -0
- package/src/entities/short_url.test.ts +7 -0
- package/src/entities/short_url.ts +36 -0
- package/src/entities/subscription_plan.test.ts +7 -0
- package/src/entities/subscription_plan.ts +73 -0
- package/src/entities/supply_domain.test.ts +7 -0
- package/src/entities/supply_domain.ts +27 -0
- package/src/entities/system_role.test.ts +7 -0
- package/src/entities/system_role.ts +17 -0
- package/src/entities/theme.test.ts +38 -0
- package/src/entities/theme.ts +267 -0
- package/src/entities/theme_css_setting.test.ts +7 -0
- package/src/entities/theme_css_setting.ts +23 -0
- package/src/entities/user.test.ts +154 -0
- package/src/entities/user.ts +394 -0
- package/src/entities/user_company.test.ts +7 -0
- package/src/entities/user_company.ts +27 -0
- package/src/entities/variation.test.ts +7 -0
- package/src/entities/variation.ts +61 -0
- package/src/entities/variation_field.test.ts +76 -0
- package/src/entities/variation_field.ts +169 -0
- package/src/entities/variation_fields_option.test.ts +46 -0
- package/src/entities/variation_fields_option.ts +95 -0
- package/src/entities/variation_option.test.ts +7 -0
- package/src/entities/variation_option.ts +52 -0
- package/src/entities/variations_group.test.ts +7 -0
- package/src/entities/variations_group.ts +41 -0
- package/src/entity.ts +1002 -0
- package/src/merchi.test.ts +25 -0
- package/src/merchi.ts +379 -0
- package/src/request.test.ts +43 -0
- package/src/request.ts +124 -0
- package/src/test_util.ts +18 -0
- package/src/toasts.test.ts +38 -0
- package/src/toasts.ts +34 -0
- package/src/util/float.ts +12 -0
- package/src/util/query_string.test.ts +7 -0
- package/src/util/query_string.ts +11 -0
- package/src/util/validation.test.ts +16 -0
- package/src/util/validation.ts +2 -0
- package/src/uuid.test.ts +15 -0
- package/src/uuid.ts +8 -0
- package/tsconfig.json +23 -0
- 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
|
+
}
|