ti2-bokun 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,356 @@
1
+ const { makeExecutableSchema } = require('@graphql-tools/schema');
2
+ const R = require('ramda');
3
+ const moment = require('moment');
4
+ const { graphql } = require('graphql');
5
+
6
+ /** Normalize date to ISO 8601 so UI's moment().utcOffset().format() works reliably */
7
+ const toIsoString = val => {
8
+ if (!val) return null;
9
+ if (typeof val !== 'string' && typeof val !== 'number') return null;
10
+ const m = moment(val);
11
+ return m.isValid() ? m.toISOString() : null;
12
+ };
13
+
14
+ const resolvers = {
15
+ Query: {
16
+ id: o => R.path(['id'], o) || R.path(['confirmationCode'], o),
17
+ bookingId: o => R.path(['id'], o) || R.path(['confirmationCode'], o),
18
+ supplierReference: o => R.path(['supplierReference'], o) || R.path(['confirmationCode'], o) || R.path(['id'], o),
19
+ supplierBookingId: o => R.path(['supplierReference'], o) || R.path(['confirmationCode'], o) || R.path(['id'], o),
20
+ status: o => {
21
+ const status = R.path(['status'], o);
22
+ const bookingStatus = R.path(['bookingStatus'], o);
23
+
24
+ if (status) return status;
25
+ if (bookingStatus) {
26
+ switch (bookingStatus.toUpperCase()) {
27
+ case 'CONFIRMED': return 'CONFIRMED';
28
+ case 'CANCELLED': return 'CANCELLED';
29
+ case 'PENDING': return 'PENDING';
30
+ case 'EXPIRED': return 'EXPIRED';
31
+ default: return 'PENDING';
32
+ }
33
+ }
34
+
35
+ return 'CONFIRMED';
36
+ },
37
+ productId: o => {
38
+ const productId = R.path(['productId'], o);
39
+ if (productId) return productId;
40
+
41
+ const activityBookings = R.path(['activityBookings'], o);
42
+ if (activityBookings && activityBookings.length > 0) {
43
+ return activityBookings[0].activityId;
44
+ }
45
+
46
+ return null;
47
+ },
48
+ productName: o => {
49
+ const productName = R.path(['productName'], o);
50
+ const productTitle = R.path(['productTitle'], o);
51
+ const product = R.path(['product'], o);
52
+
53
+ if (productName) return productName;
54
+ if (productTitle) return productTitle;
55
+ if (product && product.internalName) return product.internalName;
56
+
57
+ const activityBookings = R.path(['activityBookings'], o);
58
+ if (activityBookings && activityBookings.length > 0) {
59
+ return activityBookings[0].activityTitle || activityBookings[0].activityName;
60
+ }
61
+
62
+ return 'Bokun Activity';
63
+ },
64
+ cancellable: o => {
65
+ const cancellable = R.path(['cancellable'], o);
66
+ const status = R.path(['status'], o) || R.path(['bookingStatus'], o);
67
+
68
+ if (cancellable !== undefined) return cancellable;
69
+
70
+ return status !== 'CANCELLED' && status !== 'EXPIRED';
71
+ },
72
+ editable: o => {
73
+ const editable = R.path(['editable'], o);
74
+ const status = R.path(['status'], o) || R.path(['bookingStatus'], o);
75
+
76
+ if (editable !== undefined) return editable;
77
+
78
+ return status !== 'CANCELLED' && status !== 'EXPIRED';
79
+ },
80
+ unitItems: o => {
81
+ const unitItems = R.path(['unitItems'], o);
82
+ if (unitItems && unitItems.length > 0) {
83
+ // OCTO format: aggregate by unitId (each unitItem = 1 unit)
84
+ const byUnitId = unitItems.reduce((acc, item) => {
85
+ const unitId = item.unitId || (item.unit && item.unit.id);
86
+ const unitName = (item.unit && item.unit.internalName) || item.unitName || 'Ticket';
87
+ if (unitId) {
88
+ if (!acc[unitId]) acc[unitId] = { unitItemId: unitId, unitId, unitName, quantity: 0 };
89
+ acc[unitId].quantity += 1;
90
+ }
91
+ return acc;
92
+ }, {});
93
+ return Object.values(byUnitId);
94
+ }
95
+
96
+ const activityBookings = R.path(['activityBookings'], o);
97
+ if (activityBookings && activityBookings.length > 0) {
98
+ const pricingCategoryBookings = activityBookings[0].pricingCategoryBookings || [];
99
+ return pricingCategoryBookings.map(booking => ({
100
+ unitItemId: booking.pricingCategoryId,
101
+ unitId: booking.pricingCategoryId,
102
+ unitName: booking.pricingCategoryName || booking.pricingCategoryTitle || 'Ticket',
103
+ quantity: booking.quantity || 1,
104
+ }));
105
+ }
106
+
107
+ return [];
108
+ },
109
+ holder: o => {
110
+ const holder = R.path(['holder'], o);
111
+ if (holder) return holder;
112
+
113
+ const contact = R.path(['contact'], o);
114
+ const customerInfo = R.path(['customerInfo'], o);
115
+ const customer = R.path(['customer'], o);
116
+ const info = contact || customerInfo || customer;
117
+
118
+ if (info) {
119
+ const firstName = info.firstName || (info.fullName || '').split(' ')[0] || '';
120
+ const lastName = info.lastName || (info.fullName || '').split(' ').slice(1).join(' ') || '';
121
+ const fullName = `${info.firstName || ''} ${info.lastName || ''}`.trim()
122
+ || info.fullName || info.name || null;
123
+ return {
124
+ name: firstName || (fullName ? fullName.split(' ')[0] : null) || null,
125
+ surname: lastName || (fullName ? fullName.split(' ').slice(1).join(' ') : null) || null,
126
+ fullName: fullName || (firstName || lastName ? `${firstName} ${lastName}`.trim() : null) || null,
127
+ emailAddress: info.email || info.emailAddress || null,
128
+ phoneNumber: info.phoneNumber || info.phone || null,
129
+ locales: info.locales || [],
130
+ country: info.country || null,
131
+ };
132
+ }
133
+
134
+ return null;
135
+ },
136
+ notes: o => R.path(['notes'], o) || R.path(['specialRequests'], o) || null,
137
+ price: o => {
138
+ const price = R.path(['price'], o);
139
+ const pricing = R.path(['pricing'], o);
140
+ const totalPrice = R.path(['totalPrice'], o);
141
+ const total = R.path(['total'], o);
142
+
143
+ if (price) return price;
144
+ if (pricing) {
145
+ return {
146
+ original: pricing.original != null ? pricing.original : (pricing.retail != null ? pricing.retail : 0),
147
+ retail: pricing.retail != null ? pricing.retail : (pricing.original != null ? pricing.original : 0),
148
+ net: pricing.net != null ? pricing.net : (pricing.retail != null ? pricing.retail : (pricing.original != null ? pricing.original : 0)),
149
+ currency: pricing.currency || 'USD',
150
+ currencyPrecision: pricing.currencyPrecision != null ? pricing.currencyPrecision : 2,
151
+ };
152
+ }
153
+ if (totalPrice) {
154
+ return {
155
+ original: totalPrice,
156
+ retail: totalPrice,
157
+ net: totalPrice,
158
+ currency: R.path(['currency'], o) || 'USD',
159
+ currencyPrecision: 2,
160
+ };
161
+ }
162
+ if (total) {
163
+ return {
164
+ original: total,
165
+ retail: total,
166
+ net: total,
167
+ currency: R.path(['currency'], o) || 'USD',
168
+ currencyPrecision: 2,
169
+ };
170
+ }
171
+
172
+ return {
173
+ original: 0,
174
+ retail: 0,
175
+ net: 0,
176
+ currency: 'USD',
177
+ currencyPrecision: 2,
178
+ };
179
+ },
180
+ cancelPolicy: o => R.path(['cancelPolicy'], o) || R.path(['cancellationPolicy'], o) || null,
181
+ optionId: o => {
182
+ const optionId = R.path(['optionId'], o);
183
+ if (optionId) return optionId;
184
+
185
+ const activityBookings = R.path(['activityBookings'], o);
186
+ if (activityBookings && activityBookings.length > 0) {
187
+ return activityBookings[0].rateId || activityBookings[0].startTimeId;
188
+ }
189
+
190
+ return null;
191
+ },
192
+ optionName: o => {
193
+ const optionName = R.path(['optionName'], o);
194
+ const option = R.path(['option'], o);
195
+ if (optionName) return optionName;
196
+ if (option && option.internalName) return option.internalName;
197
+
198
+ const activityBookings = R.path(['activityBookings'], o);
199
+ if (activityBookings && activityBookings.length > 0) {
200
+ return activityBookings[0].rateName || activityBookings[0].startTime;
201
+ }
202
+
203
+ return null;
204
+ },
205
+ availabilityId: o => {
206
+ const availabilityId = R.path(['availabilityId'], o);
207
+ if (availabilityId) return availabilityId;
208
+
209
+ const activityBookings = R.path(['activityBookings'], o);
210
+ if (activityBookings && activityBookings.length > 0) {
211
+ return activityBookings[0].startTimeId;
212
+ }
213
+
214
+ return null;
215
+ },
216
+ utcDateTimeStart: o => {
217
+ const start = R.path(['utcDateTimeStart'], o)
218
+ || R.path(['availability', 'localDateTimeStart'], o)
219
+ || R.path(['startDate'], o);
220
+ return toIsoString(start);
221
+ },
222
+ utcDateTimeEnd: o => {
223
+ const end = R.path(['utcDateTimeEnd'], o)
224
+ || R.path(['availability', 'localDateTimeEnd'], o)
225
+ || R.path(['endDate'], o);
226
+ return toIsoString(end);
227
+ },
228
+ timeZone: o => R.path(['product', 'timeZone'], o) || null,
229
+ start: o => {
230
+ const start = R.path(['utcDateTimeStart'], o)
231
+ || R.path(['availability', 'localDateTimeStart'], o)
232
+ || R.path(['startDate'], o);
233
+ return toIsoString(start);
234
+ },
235
+ end: o => {
236
+ const end = R.path(['utcDateTimeEnd'], o)
237
+ || R.path(['availability', 'localDateTimeEnd'], o)
238
+ || R.path(['endDate'], o);
239
+ return toIsoString(end);
240
+ },
241
+ bookingDate: o => toIsoString(R.path(['utcCreatedAt'], o) || R.path(['createdDate'], o)),
242
+ utcCreatedAt: o => toIsoString(R.path(['utcCreatedAt'], o) || R.path(['createdDate'], o)),
243
+ utcUpdatedAt: o => toIsoString(R.path(['utcUpdatedAt'], o) || R.path(['modifiedDate'], o)),
244
+ utcExpiresAt: o => toIsoString(R.path(['utcExpiresAt'], o) || R.path(['expiresAt'], o)),
245
+ utcRedeemedAt: o => toIsoString(R.path(['utcRedeemedAt'], o)),
246
+ utcConfirmedAt: o => toIsoString(R.path(['utcConfirmedAt'], o) || R.path(['confirmedDate'], o)),
247
+ resellerReference: o => R.path(['resellerReference'], o) || R.path(['externalSaleId'], o) || null,
248
+ publicUrl: o => R.path(['publicUrl'], o) || null,
249
+ privateUrl: o => {
250
+ const url = R.path(['privateUrl'], o);
251
+ if (url) return url;
252
+ const uiBaseUrl = R.path(['uiBaseUrl'], o);
253
+ const id = R.path(['id'], o);
254
+ if (uiBaseUrl && id) {
255
+ const base = uiBaseUrl.replace(/\/$/, '');
256
+ return `${base}/sales/${id}`;
257
+ }
258
+ return null;
259
+ },
260
+ pickupRequested: o => R.path(['pickupRequested'], o) || false,
261
+ pickupPointId: o => R.path(['pickupPointId'], o) || R.path(['pickupPlaceId'], o) || null,
262
+ pickupPoint: o => R.path(['pickupPoint'], o) || R.path(['pickupPlace'], o) || null,
263
+ pickupHotel: o => R.path(['pickupHotel'], o) || null,
264
+ pickupHotelRoom: o => R.path(['pickupHotelRoom'], o) || null,
265
+ voucher: o => {
266
+ const voucher = R.path(['voucher'], o);
267
+ if (!voucher) return null;
268
+ if (typeof voucher === 'string') return voucher;
269
+ const options = voucher.deliveryOptions || [];
270
+ const pdf = options.find(opt => opt.deliveryFormat === 'PDF_URL');
271
+ return (pdf && pdf.deliveryValue) || null;
272
+ },
273
+ },
274
+ };
275
+
276
+ const getSchemaTypeNames = typeDefs => {
277
+ const names = new Set();
278
+ const strings = Array.isArray(typeDefs) ? typeDefs : [typeDefs];
279
+ const typeRegex = /type\s+([A-Za-z0-9_]+)\s*\{([^}]*)\}/g;
280
+
281
+ strings.forEach(def => {
282
+ if (typeof def !== 'string') return;
283
+ let match = typeRegex.exec(def);
284
+ while (match) {
285
+ names.add(match[1]);
286
+ match = typeRegex.exec(def);
287
+ }
288
+ });
289
+
290
+ return names;
291
+ };
292
+
293
+ const getTypeFields = typeDefs => {
294
+ const typeFields = {};
295
+ const strings = Array.isArray(typeDefs) ? typeDefs : [typeDefs];
296
+ const typeRegex = /type\s+([A-Za-z0-9_]+)\s*\{([^}]*)\}/g;
297
+ const fieldRegex = /([A-Za-z0-9_]+)\s*[:([]/g;
298
+
299
+ strings.forEach(def => {
300
+ if (typeof def !== 'string') return;
301
+ let match = typeRegex.exec(def);
302
+ while (match) {
303
+ const typeName = match[1];
304
+ const body = match[2];
305
+ const fields = new Set();
306
+ let fieldMatch = fieldRegex.exec(body);
307
+ while (fieldMatch) {
308
+ fields.add(fieldMatch[1]);
309
+ fieldMatch = fieldRegex.exec(body);
310
+ }
311
+ typeFields[typeName] = fields;
312
+ match = typeRegex.exec(def);
313
+ }
314
+ });
315
+
316
+ return typeFields;
317
+ };
318
+
319
+ const translateBooking = async ({
320
+ rootValue,
321
+ typeDefs,
322
+ query,
323
+ }) => {
324
+ const schemaTypeNames = getSchemaTypeNames(typeDefs);
325
+ const typeFields = getTypeFields(typeDefs);
326
+
327
+ const filteredResolvers = {};
328
+ if (schemaTypeNames.has('Query')) {
329
+ const queryFields = typeFields.Query || new Set();
330
+ const queryResolvers = {};
331
+ Object.keys(resolvers.Query).forEach(fieldName => {
332
+ if (queryFields.has(fieldName)) {
333
+ queryResolvers[fieldName] = resolvers.Query[fieldName];
334
+ }
335
+ });
336
+ if (Object.keys(queryResolvers).length > 0) {
337
+ filteredResolvers.Query = queryResolvers;
338
+ }
339
+ }
340
+
341
+ const schema = makeExecutableSchema({
342
+ typeDefs,
343
+ resolvers: filteredResolvers,
344
+ });
345
+ const retVal = await graphql({
346
+ schema,
347
+ rootValue,
348
+ source: query,
349
+ });
350
+ if (retVal.errors) throw new Error(retVal.errors);
351
+ return retVal.data;
352
+ };
353
+
354
+ module.exports = {
355
+ translateBooking,
356
+ };
@@ -0,0 +1,158 @@
1
+ const { makeExecutableSchema } = require('@graphql-tools/schema');
2
+ const R = require('ramda');
3
+ const { graphql } = require('graphql');
4
+
5
+ const buildPickupResolvers = typeDefs => {
6
+ const resolvers = {};
7
+
8
+ const pickupFields = {
9
+ key: o => {
10
+ const id = R.path(['id'], o) || R.path(['uuid'], o) || R.path(['code'], o);
11
+ const lat = R.path(['latitude'], o) || R.path(['lat'], o) || R.path(['geoPoint', 'latitude'], o);
12
+ const lng = R.path(['longitude'], o) || R.path(['lng'], o)
13
+ || R.path(['lon'], o) || R.path(['geoPoint', 'longitude'], o);
14
+ if (id) return id.toString();
15
+ if (lat !== undefined && lng !== undefined) return `${lat},${lng}`;
16
+ return null;
17
+ },
18
+ id: R.path(['id']),
19
+ name: o => R.path(['name'], o) || R.path(['title'], o) || R.path(['label'], o),
20
+ description: o => R.path(['description'], o) || R.path(['notes'], o) || null,
21
+ address: o => {
22
+ const address = R.path(['address'], o);
23
+ if (address) return address;
24
+ const street = R.path(['street'], o);
25
+ const city = R.path(['city'], o);
26
+ const state = R.path(['state'], o);
27
+ const postalCode = R.path(['postalCode'], o) || R.path(['zipCode'], o);
28
+ const country = R.path(['country'], o);
29
+ const parts = [];
30
+ if (street) parts.push(street);
31
+ if (city) parts.push(city);
32
+ if (state) parts.push(state);
33
+ if (postalCode) parts.push(postalCode);
34
+ if (country) parts.push(country);
35
+ return parts.length > 0 ? parts.join(', ') : null;
36
+ },
37
+ latitude: o => {
38
+ const latitude = R.path(['latitude'], o);
39
+ const lat = R.path(['lat'], o);
40
+ const geoPoint = R.path(['geoPoint'], o);
41
+ if (latitude !== undefined) return latitude;
42
+ if (lat !== undefined) return lat;
43
+ if (geoPoint && geoPoint.latitude !== undefined) return geoPoint.latitude;
44
+ return null;
45
+ },
46
+ longitude: o => {
47
+ const longitude = R.path(['longitude'], o);
48
+ const lng = R.path(['lng'], o);
49
+ const lon = R.path(['lon'], o);
50
+ const geoPoint = R.path(['geoPoint'], o);
51
+ if (longitude !== undefined) return longitude;
52
+ if (lng !== undefined) return lng;
53
+ if (lon !== undefined) return lon;
54
+ if (geoPoint && geoPoint.longitude !== undefined) return geoPoint.longitude;
55
+ return null;
56
+ },
57
+ pickupType: o => {
58
+ const pickupType = R.path(['pickupType'], o);
59
+ const type = R.path(['type'], o);
60
+ if (pickupType) return pickupType;
61
+ if (type) return type;
62
+ return 'MEET_ON_LOCATION';
63
+ },
64
+ directions: o => R.path(['directions'], o) || R.path(['instructions'], o) || null,
65
+ };
66
+
67
+ const defaultTypeNames = ['PickupPoint', 'PickupPointNode', 'PickupLocation'];
68
+
69
+ const strings = (Array.isArray(typeDefs) ? typeDefs : [typeDefs]).filter(Boolean);
70
+ const typeRegex = /type\s+([A-Za-z0-9_]+)\s*\{([^}]*)\}/g;
71
+
72
+ strings.forEach(def => {
73
+ if (typeof def !== 'string') return;
74
+ let match = typeRegex.exec(def);
75
+ while (match) {
76
+ const [, typeName, body] = match;
77
+ if (/pickup/i.test(typeName) || /pickup/i.test(body)) {
78
+ const fieldResolvers = {};
79
+ const fieldRegex = /([A-Za-z0-9_]+)\s*:/g;
80
+ let fieldMatch = fieldRegex.exec(body);
81
+ while (fieldMatch) {
82
+ const fieldName = fieldMatch[1];
83
+ if (pickupFields[fieldName]) {
84
+ fieldResolvers[fieldName] = pickupFields[fieldName];
85
+ }
86
+ fieldMatch = fieldRegex.exec(body);
87
+ }
88
+
89
+ if (Object.keys(fieldResolvers).length > 0) {
90
+ resolvers[typeName] = fieldResolvers;
91
+ }
92
+ }
93
+ match = typeRegex.exec(def);
94
+ }
95
+ });
96
+
97
+ if (Object.keys(resolvers).length === 0) {
98
+ defaultTypeNames.forEach(typeName => {
99
+ resolvers[typeName] = pickupFields;
100
+ });
101
+ }
102
+
103
+ return resolvers;
104
+ };
105
+
106
+ const translatePickupPoint = async ({
107
+ rootValue,
108
+ typeDefs,
109
+ query,
110
+ }) => {
111
+ const resolvers = buildPickupResolvers(typeDefs);
112
+
113
+ const typeNames = Object.keys(resolvers);
114
+ const strings = (Array.isArray(typeDefs) ? typeDefs : [typeDefs]).filter(Boolean).slice();
115
+
116
+ const hasTypeDefinition = (defs, typeName) => defs.some(
117
+ def => typeof def === 'string' && new RegExp(`type\\s+${typeName}\\b`).test(def),
118
+ );
119
+
120
+ const getFieldType = fieldName => {
121
+ if (fieldName === 'key' || fieldName === 'id') return 'ID';
122
+ if (fieldName === 'latitude' || fieldName === 'longitude') return 'Float';
123
+ return 'String';
124
+ };
125
+
126
+ typeNames.forEach(typeName => {
127
+ if (!hasTypeDefinition(strings, typeName)) {
128
+ const availableFields = Object.keys(resolvers[typeName]);
129
+ const typeDef = `type ${typeName} {\n${
130
+ availableFields.map(field => ` ${field}: ${getFieldType(field)}`).join('\n')
131
+ }\n}`;
132
+ strings.push(typeDef);
133
+ }
134
+ });
135
+
136
+ if (!hasTypeDefinition(strings, 'Query')) {
137
+ strings.push('type Query { _pickupPointDummy: Boolean }');
138
+ }
139
+
140
+ if (!query) {
141
+ return {};
142
+ }
143
+
144
+ const retVal = await graphql({
145
+ schema: makeExecutableSchema({
146
+ typeDefs: strings,
147
+ resolvers,
148
+ }),
149
+ rootValue: rootValue || {},
150
+ source: query,
151
+ });
152
+ if (retVal.errors) throw new Error(retVal.errors);
153
+ return retVal.data;
154
+ };
155
+
156
+ module.exports = {
157
+ translatePickupPoint,
158
+ };
@@ -0,0 +1,155 @@
1
+ const { makeExecutableSchema } = require('@graphql-tools/schema');
2
+ const R = require('ramda');
3
+ const { graphql } = require('graphql');
4
+
5
+ const resolvers = {
6
+ Query: {
7
+ productId: R.path(['id']),
8
+ productName: o => R.path(['title'], o) || R.path(['name'], o) || R.prop('internalName', o),
9
+ availableCurrencies: o => {
10
+ const currencies = R.path(['currencies'], o);
11
+ return currencies || ['USD'];
12
+ },
13
+ defaultCurrency: o => R.path(['defaultCurrency'], o) || 'USD',
14
+ options: root => {
15
+ const products = R.propOr([], 'products', root);
16
+ const options = R.propOr([], 'options', root);
17
+ const rates = R.propOr([], 'rates', root);
18
+
19
+ if (products.length > 0) {
20
+ return products.map(product => ({
21
+ ...product,
22
+ id: product.id,
23
+ title: product.title || product.name,
24
+ units: product.pricingCategories || [],
25
+ }));
26
+ }
27
+
28
+ if (options.length > 0) {
29
+ return options;
30
+ }
31
+
32
+ if (rates.length > 0) {
33
+ return [{
34
+ id: root.id,
35
+ title: root.title || root.name,
36
+ units: rates,
37
+ }];
38
+ }
39
+
40
+ return [{
41
+ id: root.id,
42
+ title: root.title || root.name,
43
+ units: R.propOr([], 'pricingCategories', root),
44
+ }];
45
+ },
46
+ },
47
+ Option: {
48
+ optionId: R.prop('id'),
49
+ optionName: o => R.path(['title'], o) || R.path(['name'], o) || R.prop('internalName', o),
50
+ units: root => {
51
+ const units = R.propOr([], 'units', root);
52
+ const pricingCategories = R.propOr([], 'pricingCategories', root);
53
+ const rates = R.propOr([], 'rates', root);
54
+
55
+ if (units.length > 0) return units;
56
+ if (pricingCategories.length > 0) return pricingCategories;
57
+ if (rates.length > 0) return rates;
58
+
59
+ return [];
60
+ },
61
+ },
62
+ Unit: {
63
+ unitId: o => R.path(['id'], o) || R.path(['pricingCategoryId'], o),
64
+ unitName: o => R.path(['title'], o) || R.path(['name'], o) || R.path(['label'], o) || R.prop('internalName', o),
65
+ subtitle: R.path(['subtitle']),
66
+ type: o => {
67
+ const ageFrom = R.path(['minAge'], o);
68
+ const ageTo = R.path(['maxAge'], o);
69
+
70
+ if (ageFrom !== undefined && ageTo !== undefined) {
71
+ if (ageFrom >= 18) return 'ADULT';
72
+ if (ageTo <= 17) return 'CHILD';
73
+ if (ageTo <= 3) return 'INFANT';
74
+ }
75
+
76
+ return R.prop('type', o) || 'ADULT';
77
+ },
78
+ pricing: root => {
79
+ const pricing = R.path(['pricing'], root);
80
+ const price = R.path(['price'], root);
81
+ const defaultPrice = R.path(['defaultPrice'], root);
82
+ const priceFrom = R.path(['priceFrom'], root);
83
+
84
+ if (pricing) return pricing;
85
+
86
+ if (price !== undefined) {
87
+ return [{
88
+ currency: R.path(['currency'], root) || 'USD',
89
+ currencyPrecision: 2,
90
+ price,
91
+ }];
92
+ }
93
+
94
+ if (defaultPrice !== undefined) {
95
+ return [{
96
+ currency: R.path(['currency'], root) || 'USD',
97
+ currencyPrecision: 2,
98
+ price: defaultPrice,
99
+ }];
100
+ }
101
+
102
+ if (priceFrom !== undefined) {
103
+ return [{
104
+ currency: R.path(['currency'], root) || 'USD',
105
+ currencyPrecision: 2,
106
+ price: priceFrom,
107
+ }];
108
+ }
109
+
110
+ return [{
111
+ currency: 'USD',
112
+ currencyPrecision: 2,
113
+ price: 0,
114
+ }];
115
+ },
116
+ restrictions: root => {
117
+ const restrictions = R.prop('restrictions', root);
118
+ if (restrictions) return restrictions;
119
+
120
+ const minAge = R.path(['minAge'], root);
121
+ const maxAge = R.path(['maxAge'], root);
122
+ const minQuantity = R.path(['minQuantity'], root);
123
+ const maxQuantity = R.path(['maxQuantity'], root);
124
+
125
+ return {
126
+ ...(minAge !== undefined && { minAge }),
127
+ ...(maxAge !== undefined && { maxAge }),
128
+ ...(minQuantity !== undefined && { minUnits: minQuantity }),
129
+ ...(maxQuantity !== undefined && { maxUnits: maxQuantity }),
130
+ };
131
+ },
132
+ },
133
+ };
134
+
135
+ const translateProduct = async ({
136
+ rootValue,
137
+ typeDefs,
138
+ query,
139
+ }) => {
140
+ const schema = makeExecutableSchema({
141
+ typeDefs,
142
+ resolvers,
143
+ });
144
+ const retVal = await graphql({
145
+ schema,
146
+ rootValue,
147
+ source: query,
148
+ });
149
+ if (retVal.errors) throw new Error(retVal.errors);
150
+ return retVal.data;
151
+ };
152
+
153
+ module.exports = {
154
+ translateProduct,
155
+ };
@@ -0,0 +1,33 @@
1
+ const { makeExecutableSchema } = require('@graphql-tools/schema');
2
+ const R = require('ramda');
3
+ const { graphql } = require('graphql');
4
+
5
+ const resolvers = {
6
+ Query: {
7
+ rateId: R.path(['id']),
8
+ rateName: o => R.path(['name'], o) || R.path(['title'], o) || R.path(['label'], o),
9
+ rateDescription: o => R.path(['description'], o) || null,
10
+ },
11
+ };
12
+
13
+ const translateRate = async ({
14
+ rootValue,
15
+ typeDefs,
16
+ query,
17
+ }) => {
18
+ const schema = makeExecutableSchema({
19
+ typeDefs,
20
+ resolvers,
21
+ });
22
+ const retVal = await graphql({
23
+ schema,
24
+ rootValue,
25
+ source: query,
26
+ });
27
+ if (retVal.errors) throw new Error(retVal.errors);
28
+ return retVal.data;
29
+ };
30
+
31
+ module.exports = {
32
+ translateRate,
33
+ };