ti2 1.0.10 → 1.0.11

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/README.md CHANGED
@@ -37,10 +37,10 @@ Some environment variables are required for ti2 to start
37
37
 
38
38
  ### Starting your server
39
39
 
40
- You can start your own instance by adding a requirement on your own nodejs project as follows:
40
+ You can start your own instance by instlling ti2 and any aplugin syou may want to use on your own nodejs project root folter:
41
41
 
42
42
  ```bash
43
- $ npm i ti2
43
+ $ npm i ti2 ti2-travelgate ti2-ventrata ti2-tourconnect
44
44
  ```
45
45
 
46
46
  To start the server with default values use the following:
@@ -67,12 +67,31 @@ Plugin ENV variable name convention: ti2_pluginName_environmenVariableName, for
67
67
  ti2_tourconnect_apiUrl=http://backend:8080
68
68
  ```
69
69
 
70
- ### Available Plugins
71
-
72
- | Repo | Features | Maintainer |
73
- | ---- | -------- | ---------- |
74
- | [ti2-ventrata](https://github.com/TourConnect/ti2-ventrata) | Bookings: Search, Cancellation | TourConnect |
75
- | [ti2-travelgate](https://github.com/TourConnect/ti2-travelgate) | Bookings: Search, Cancellation | TourConnect |
70
+ ### Plugin Library
71
+
72
+ | Methods | [Ventrata](https://github.com/TourConnect/ti2-ventrata) | [TravelGateX](https://github.com/TourConnect/ti2-travelgate) | [Didgigo](https://github.com/TourConnect/ti2-didgigo) | [TourConnect](https://github.com/TourConnect/ti2-tourconnect)
73
+ | ---- | -------- | ---------- | ---- | ---- |
74
+ |**Base**|
75
+ |validateToken|||✓|✓|
76
+ |copyMedia||||✓|
77
+ |**Content**|
78
+ |getProfile|||✓|✓|
79
+ |updateProfile||||✓|
80
+ |getLocations|||✓|✓|
81
+ |getLocation|||✓|✓|
82
+ |createLocation|||✓|✓|
83
+ |updateLocation|||✓|✓|
84
+ |getProducts|||✓|✓|
85
+ |getProduct|||✓|✓|
86
+ |createProduct|||✓|✓|
87
+ |updateProduct|||✓|✓|
88
+ |**Bookings**|
89
+ |searchProducts|✓|✓|
90
+ |searchBooking|✓|✓|
91
+ |searchAvailability|✓|✓|
92
+ |quoteAvailability|✓|✓|
93
+ |createBooking|✓|✓|
94
+ |cancelBooking|✓|✓|
76
95
 
77
96
  ## API logging
78
97
 
@@ -111,7 +130,7 @@ Feel free to check the [Issues Page](https://github.com/TourConnect/ti2/issues).
111
130
 
112
131
  ## License
113
132
 
114
- Distributed under the GPL-3 License. See LICENSE.txt for more information.
133
+ Distributed under the GPL-3 License. See LICENSE file for more information.
115
134
 
116
135
  TL;DR Here's what the license entails:
117
136
 
package/api.yml CHANGED
@@ -141,6 +141,7 @@ components:
141
141
  type: string
142
142
  bookingSearchQuery:
143
143
  type: object
144
+ additionalProperties: true
144
145
  properties:
145
146
  bookingId:
146
147
  type: string
@@ -172,6 +173,7 @@ components:
172
173
  default: "DD/MM/YYYY"
173
174
  bookingDetail:
174
175
  type: object
176
+ additionalProperties: true
175
177
  properties:
176
178
  id:
177
179
  type: string
@@ -181,7 +183,15 @@ components:
181
183
  type: string
182
184
  required: true
183
185
  description: The booking status
184
-
186
+ bookingSearchQueryResults:
187
+ type: object
188
+ additionalProperties: true
189
+ properties:
190
+ bookings:
191
+ type: object
192
+ required: true
193
+ items:
194
+ $ref: '#/components/schemas/bookingDetail'
185
195
  bookingDetailsForCancel:
186
196
  allOf: # Combines the BasicErrorModel and the inline model
187
197
  - $ref: '#/components/schemas/bookingDetail'
@@ -191,6 +201,89 @@ components:
191
201
  type: integer
192
202
  required: true
193
203
  description: The unix time the cancellation was sent
204
+ cancellationReturn:
205
+ type: object
206
+ properties:
207
+ cancellation:
208
+ type: object
209
+ allOf: # Combines the BasicErrorModel and the inline model
210
+ - $ref: '#/components/schemas/bookingDetail'
211
+ bookingsProductQuery:
212
+ type: object
213
+ additionalProperties: true
214
+ description: Additional properties may be included
215
+ properties:
216
+ productName:
217
+ type: string
218
+ required: false
219
+ description: Product Name wildcard match
220
+ example: '*bus*'
221
+ bookingsProductSearchReturn:
222
+ type: object
223
+ additionalProperties: true
224
+ description: Additional properties may be included
225
+ properties:
226
+ products:
227
+ type: array
228
+ required: false
229
+ description: Product Search Results
230
+ items:
231
+ type: object
232
+ accommodation:
233
+ type: array
234
+ required: false,
235
+ decription: Accommodation search results
236
+ items:
237
+ type: object
238
+ pricingItem:
239
+ type: object
240
+ additionalProperties: true
241
+ properties:
242
+ original:
243
+ type: integer
244
+ retail:
245
+ type: integer
246
+ net:
247
+ type: integer
248
+ currency:
249
+ type: string
250
+ example: "USD"
251
+ currencyPrecision:
252
+ type: integer
253
+ includedTaxes:
254
+ type: array
255
+ items:
256
+ type: object
257
+ additionalProperties: true
258
+ properties:
259
+ name:
260
+ type: string
261
+ retail:
262
+ type: integer
263
+ net:
264
+ type: integer
265
+ availabilityItem:
266
+ type: object
267
+ additionalProperties: true
268
+ properties:
269
+ id:
270
+ type: string
271
+ required: true
272
+ description: An availability Id that can be used for quoting
273
+ pricing:
274
+ $ref: '#/components/schemas/pricingItem'
275
+ required: false
276
+ cancelPolicy:
277
+ type: string
278
+ required: false
279
+ bookingRequest:
280
+ type: object
281
+ additionalProperties: true
282
+ required: true
283
+ properties:
284
+ id:
285
+ type: string
286
+ description: a quote Id to make a reservation for
194
287
  paths:
195
288
  /ping:
196
289
  get:
@@ -516,10 +609,12 @@ paths:
516
609
  schema:
517
610
  $ref: '#/components/schemas/bookingSearchQuery'
518
611
  responses:
519
- description: search results
520
- content:
521
- application/json:
522
- schema: '#/components/schemas/AnyValue'
612
+ '200':
613
+ description: search results
614
+ content:
615
+ application/json:
616
+ schema:
617
+ $ref: '#/components/schemas/bookingSearchQueryResults'
523
618
  /bookings/{appKey}/{userId}/{hint}/search:
524
619
  post:
525
620
  security:
@@ -557,10 +652,12 @@ paths:
557
652
  schema:
558
653
  $ref: '#/components/schemas/bookingSearchQuery'
559
654
  responses:
560
- description: search results
561
- content:
562
- application/json:
563
- schema: '#/components/schemas/AnyValue'
655
+ '200':
656
+ description: search results
657
+ content:
658
+ application/json:
659
+ schema:
660
+ $ref: '#/components/schemas/bookingSearchQueryResults'
564
661
  /bookings/{appKey}/{userId}/cancel:
565
662
  post:
566
663
  security:
@@ -591,10 +688,12 @@ paths:
591
688
  schema:
592
689
  $ref: '#/components/schemas/bookingDetailsForCancel'
593
690
  responses:
594
- description: search results
595
- content:
596
- application/json:
597
- schema: '#/components/schemas/AnyValue'
691
+ '200':
692
+ description: Cancellation confirmation details
693
+ content:
694
+ application/json:
695
+ schema:
696
+ $ref: '#/components/schemas/cancellationReturn'
598
697
  /bookings/{appKey}/{userId}/{hint}/cancel:
599
698
  post:
600
699
  security:
@@ -632,7 +731,373 @@ paths:
632
731
  schema:
633
732
  $ref: '#/components/schemas/bookingDetailsForCancel'
634
733
  responses:
635
- description: search results
734
+ '200':
735
+ description: Cancellation confirmation details
736
+ content:
737
+ application/json:
738
+ schema:
739
+ $ref: '#/components/schemas/cancellationReturn'
740
+ /products/{appKey}/{userId}/search:
741
+ post:
742
+ security:
743
+ - bearerAuth: ['admin', 'user']
744
+ tags:
745
+ - bookings
746
+ summary: Product Search
747
+ operationId: bookingsProductSearch
748
+ parameters:
749
+ - name: appKey
750
+ in: path
751
+ description: app to search the booking products, defaults to first token
752
+ required: true
753
+ example: 'travelgate-hotelx'
754
+ schema:
755
+ type: string
756
+ - name: userId
757
+ in: path
758
+ description: the user/company that the appKey belongs to
759
+ required: true
760
+ schema:
761
+ type: string
762
+ requestBody:
763
+ description: Search query
764
+ required: true
765
+ content:
766
+ application/json:
767
+ schema:
768
+ $ref: '#/components/schemas/bookingsProductQuery'
769
+ responses:
770
+ '200':
771
+ description: search results
772
+ content:
773
+ application/json:
774
+ schema:
775
+ $ref: '#/components/schemas/bookingsProductSearchReturn'
776
+ /products/{appKey}/{userId}/{hint}/search:
777
+ post:
778
+ security:
779
+ - bearerAuth: ['admin', 'user']
780
+ tags:
781
+ - bookings
782
+ summary: Product Search using for an specific hint
783
+ operationId: bookingsProductSearch
784
+ parameters:
785
+ - name: appKey
786
+ in: path
787
+ description: app to search the booking products, defaults to first token
788
+ required: true
789
+ example: 'travelgate-hotelx'
790
+ schema:
791
+ type: string
792
+ - name: userId
793
+ in: path
794
+ description: the user/company that the appKey belongs to
795
+ required: true
796
+ schema:
797
+ type: string
798
+ - name: hint
799
+ in: path
800
+ description: a specific token hint to be used
801
+ required: true
802
+ example: 'travelgate-australia'
803
+ schema:
804
+ type: string
805
+ requestBody:
806
+ description: Search query
807
+ required: true
808
+ content:
809
+ application/json:
810
+ schema:
811
+ $ref: '#/components/schemas/bookingsProductQuery'
812
+ responses:
813
+ '200':
814
+ description: search results
815
+ content:
816
+ application/json:
817
+ schema:
818
+ $ref: '#/components/schemas/bookingsProductSearchReturn'
819
+ /bookings/{appKey}/{userId}/availability:
820
+ post:
821
+ security:
822
+ - bearerAuth: ['admin', 'user']
823
+ tags:
824
+ - bookings
825
+ summary: Availability Search
826
+ operationId: bookingsAvailabilitySearch
827
+ parameters:
828
+ - name: appKey
829
+ in: path
830
+ description: app to search availability for, defaults to first token
831
+ required: true
832
+ example: 'travelgate-hotelx'
833
+ schema:
834
+ type: string
835
+ - name: userId
836
+ in: path
837
+ description: the user/company that the appKey belongs to
838
+ required: true
839
+ schema:
840
+ type: string
841
+ requestBody:
842
+ description: both app details
843
+ required: true
844
+ content:
845
+ application/json:
846
+ schema:
847
+ $ref: '#/components/schemas/AnyValue'
848
+ responses:
849
+ '200':
850
+ description: search results
851
+ content:
852
+ application/json:
853
+ schema:
854
+ type: object
855
+ properties:
856
+ availability:
857
+ type: array
858
+ required: true
859
+ items:
860
+ $ref: '#/components/schemas/availabilityItem'
861
+ /bookings/{appKey}/{userId}/{hint}/availability:
862
+ post:
863
+ security:
864
+ - bearerAuth: ['admin', 'user']
865
+ tags:
866
+ - bookings
867
+ summary: Availability Search for an specific hint
868
+ operationId: bookingsAvailabilitySearch
869
+ parameters:
870
+ - name: appKey
871
+ in: path
872
+ description: app to search availability for, defaults to first token
873
+ required: true
874
+ example: 'travelgate-hotelx'
875
+ schema:
876
+ type: string
877
+ - name: userId
878
+ in: path
879
+ description: the user/company that the appKey belongs to
880
+ required: true
881
+ schema:
882
+ type: string
883
+ - name: hint
884
+ in: path
885
+ description: a specific token hint to be used
886
+ required: true
887
+ example: 'travelgate-australia'
888
+ schema:
889
+ type: string
890
+ requestBody:
891
+ description: both app details
892
+ required: true
893
+ content:
894
+ application/json:
895
+ schema:
896
+ $ref: '#/components/schemas/AnyValue'
897
+ responses:
898
+ '200':
899
+ description: search results
900
+ content:
901
+ application/json:
902
+ schema:
903
+ type: object
904
+ properties:
905
+ availability:
906
+ type: array
907
+ required: true
908
+ items:
909
+ $ref: '#/components/schemas/availabilityItem'
910
+ /bookings/{appKey}/{userId}/quote:
911
+ post:
912
+ security:
913
+ - bearerAuth: ['admin', 'user']
914
+ tags:
915
+ - bookings
916
+ summary: Quote an Availability
917
+ operationId: searchQuote
918
+ parameters:
919
+ - name: appKey
920
+ in: path
921
+ description: app to quote availability for, defaults to first token
922
+ required: true
923
+ example: 'travelgate-hotelx'
924
+ schema:
925
+ type: string
926
+ - name: userId
927
+ in: path
928
+ description: the user/company that the appKey belongs to
929
+ required: true
930
+ schema:
931
+ type: string
932
+ requestBody:
933
+ description: Availability spec to be quoted
934
+ required: true
636
935
  content:
637
936
  application/json:
638
- schema: '#/components/schemas/AnyValue'
937
+ schema:
938
+ type: object
939
+ additionalProperties: true
940
+ properties:
941
+ id:
942
+ type: string
943
+ required: true
944
+ description: An availability Id to quote
945
+ responses:
946
+ '200':
947
+ description: Quote Details
948
+ content:
949
+ application/json:
950
+ schema:
951
+ type: object
952
+ required: true
953
+ properties:
954
+ quote:
955
+ type: array
956
+ required: true
957
+ items:
958
+ type: object
959
+ additionalProperties: true
960
+ properties:
961
+ id:
962
+ type: string
963
+ description: a quote Id that can be use to book
964
+ /bookings/{appKey}/{userId}/{hint}/quote:
965
+ post:
966
+ security:
967
+ - bearerAuth: ['admin', 'user']
968
+ tags:
969
+ - bookings
970
+ summary: Quote an Availability for an specific hint
971
+ operationId: searchQuote
972
+ parameters:
973
+ - name: appKey
974
+ in: path
975
+ description: app to quote the availability for, defaults to first token
976
+ required: true
977
+ example: 'travelgate-hotelx'
978
+ schema:
979
+ type: string
980
+ - name: userId
981
+ in: path
982
+ description: the user/company that the appKey belongs to
983
+ required: true
984
+ schema:
985
+ type: string
986
+ - name: hint
987
+ in: path
988
+ description: a specific token hint to be used
989
+ required: true
990
+ example: 'travelgate-australia'
991
+ schema:
992
+ type: string
993
+ requestBody:
994
+ description: Availability spec to be quoted
995
+ required: true
996
+ content:
997
+ application/json:
998
+ schema:
999
+ type: object
1000
+ additionalProperties: true
1001
+ properties:
1002
+ id:
1003
+ type: string
1004
+ required: true
1005
+ description: An availability Id to quote
1006
+ responses:
1007
+ '200':
1008
+ description: Quote Details
1009
+ content:
1010
+ application/json:
1011
+ schema:
1012
+ type: object
1013
+ required: true
1014
+ properties:
1015
+ quote:
1016
+ type: array
1017
+ required: true
1018
+ items:
1019
+ type: object
1020
+ additionalProperties: true
1021
+ properties:
1022
+ id:
1023
+ type: string
1024
+ description: a quote Id that can be use to book
1025
+ /bookings/{appKey}/{userId}/booking:
1026
+ post:
1027
+ security:
1028
+ - bearerAuth: ['admin', 'user']
1029
+ tags:
1030
+ - bookings
1031
+ summary: Create a Booking
1032
+ operationId: createBooking
1033
+ parameters:
1034
+ - name: appKey
1035
+ in: path
1036
+ description: app to create the booking for, defaults to first token
1037
+ required: true
1038
+ example: 'travelgate-hotelx'
1039
+ schema:
1040
+ type: string
1041
+ - name: userId
1042
+ in: path
1043
+ description: the user/company that the appKey belongs to
1044
+ required: true
1045
+ schema:
1046
+ type: string
1047
+ requestBody:
1048
+ description: Booking creation payload
1049
+ required: true
1050
+ content:
1051
+ application/json:
1052
+ schema:
1053
+ $ref: '#/components/schemas/bookingRequest'
1054
+ responses:
1055
+ '200':
1056
+ description: Booking Details
1057
+ content:
1058
+ application/json:
1059
+ schema:
1060
+ $ref: '#/components/schemas/AnyValue'
1061
+ /bookings/{appKey}/{userId}/{hint}/booking:
1062
+ post:
1063
+ security:
1064
+ - bearerAuth: ['admin', 'user']
1065
+ tags:
1066
+ - bookings
1067
+ summary: Create a Booking for an specific hint
1068
+ operationId: createBooking
1069
+ parameters:
1070
+ - name: appKey
1071
+ in: path
1072
+ description: app to create the booking for, defaults to first token
1073
+ required: true
1074
+ example: 'travelgate-hotelx'
1075
+ schema:
1076
+ type: string
1077
+ - name: userId
1078
+ in: path
1079
+ description: the user/company that the appKey belongs to
1080
+ required: true
1081
+ schema:
1082
+ type: string
1083
+ - name: hint
1084
+ in: path
1085
+ description: a specific token hint to be used
1086
+ required: true
1087
+ example: 'travelgate-australia'
1088
+ schema:
1089
+ type: string
1090
+ requestBody:
1091
+ description: Booking creation payload
1092
+ required: true
1093
+ content:
1094
+ application/json:
1095
+ schema:
1096
+ $ref: '#/components/schemas/bookingRequest'
1097
+ responses:
1098
+ '200':
1099
+ description: Booking Details
1100
+ content:
1101
+ application/json:
1102
+ schema:
1103
+ $ref: '#/components/schemas/AnyValue'
@@ -11,7 +11,7 @@ describe('user: booking search', () => {
11
11
  packageName: 'ti2-travelgate',
12
12
  adminEmail: 'engineering+travelgate@tourconnect.com',
13
13
  };
14
- let appKey = newApp.name;
14
+ const appKey = newApp.name;
15
15
  const userId = '551394be5ac58e5c76000019';
16
16
  const token = {
17
17
  endpoint: 'https://api.travelgatex.com',
@@ -77,7 +77,7 @@ describe('user: booking search', () => {
77
77
  expect(value).toBeTruthy();
78
78
  }
79
79
  });
80
- it('should be able to search a travel gate for a booking', async () => {
80
+ it('should be able to search a for a booking', async () => {
81
81
  const payload = {
82
82
  bookingId: '', supplierId: '', name: '',
83
83
  };
@@ -94,4 +94,76 @@ describe('user: booking search', () => {
94
94
  // expect(Array.isArray(retVal.bookings)).toBeTruthy();
95
95
  // expect(retVal.bookings.length > 0).toBeTruthy();
96
96
  });
97
+
98
+ it('should be able to search a for a booking product', async () => {
99
+ const payload = {};
100
+ const { products } = await doApiPost({
101
+ url: `/products/${appKey}/${userId}/search`,
102
+ token: userToken,
103
+ payload,
104
+ });
105
+ expect(plugins[0].searchProducts).toHaveBeenCalled();
106
+ expect(Array.isArray(products)).toBeTruthy();
107
+ expect(plugins[0].searchProducts.mock.calls[0][0].payload).toEqual(payload);
108
+ expect(plugins[0].searchProducts.mock.calls[0][0].token).toEqual(token);
109
+
110
+ // expect(Array.isArray(retVal.bookings)).toBeTruthy();
111
+ // expect(retVal.bookings.length > 0).toBeTruthy();
112
+ });
113
+ it('should be able to search for availabiility', async () => {
114
+ const payload = {
115
+ travelDateStart: '12/30/2025',
116
+ travelDateEnd: '01/15/2026',
117
+ dateFormat: 'MM/DD/YYYY',
118
+ occupancies: [{ paxes: [{ age: 30 }, { age: 40 }] }],
119
+ currency: 'EUR',
120
+ market: 'ES',
121
+ language: 'es',
122
+ nationality: 'ES',
123
+ };
124
+ const { availability } = await doApiPost({
125
+ url: `/bookings/${appKey}/${userId}/availability`,
126
+ token: userToken,
127
+ payload,
128
+ });
129
+ expect(plugins[0].searchAvailability).toHaveBeenCalled();
130
+ expect(Array.isArray(availability)).toBeTruthy();
131
+ expect(plugins[0].searchAvailability.mock.calls[0][0].payload).toEqual(payload);
132
+ expect(plugins[0].searchAvailability.mock.calls[0][0].token).toEqual(token);
133
+ });
134
+ it('should be able to obtain a quote', async () => {
135
+ const payload = {
136
+ id: chance.guid(), // availability result id
137
+ travelDateStart: '12/30/2025',
138
+ travelDateEnd: '01/15/2026',
139
+ dateFormat: 'MM/DD/YYYY',
140
+ occupancies: [{ paxes: [{ age: 30 }, { age: 40 }] }],
141
+ currency: 'EUR',
142
+ market: 'ES',
143
+ language: 'es',
144
+ nationality: 'ES',
145
+ };
146
+ const { quote } = await doApiPost({
147
+ url: `/bookings/${appKey}/${userId}/quote`,
148
+ token: userToken,
149
+ payload,
150
+ });
151
+ expect(plugins[0].searchQuote).toHaveBeenCalled();
152
+ expect(Array.isArray(quote)).toBeTruthy();
153
+ expect(plugins[0].searchQuote.mock.calls[0][0].payload).toEqual(payload);
154
+ expect(plugins[0].searchQuote.mock.calls[0][0].token).toEqual(token);
155
+ });
156
+ it('should be able create a booking', async () => {
157
+ const payload = {
158
+ id: chance.guid(),
159
+ };
160
+ await doApiPost({
161
+ url: `/bookings/${appKey}/${userId}/booking`,
162
+ token: userToken,
163
+ payload,
164
+ });
165
+ expect(plugins[0].createBooking).toHaveBeenCalled();
166
+ expect(plugins[0].createBooking.mock.calls[0][0].payload).toEqual(payload);
167
+ expect(plugins[0].createBooking.mock.calls[0][0].token).toEqual(token);
168
+ });
97
169
  });
@@ -25,7 +25,6 @@ const bookingsSearch = plugins => async (req, res, next) => {
25
25
  });
26
26
  return res.json(results);
27
27
  } catch (err) {
28
- console.log({ err });
29
28
  return next(err);
30
29
  }
31
30
  };
@@ -57,7 +56,121 @@ const bookingsCancel = plugins => async (req, res, next) => {
57
56
  }
58
57
  };
59
58
 
59
+ const bookingsProductSearch = plugins => async (req, res, next) => {
60
+ const {
61
+ params: { appKey, userId, hint },
62
+ body: payload,
63
+ } = req;
64
+ try {
65
+ const app = plugins.find(({ name }) => name === appKey);
66
+ // const app = load(appKey);
67
+ const userAppKeys = (await UserAppKey.findOne({
68
+ where: {
69
+ userId,
70
+ integrationId: appKey,
71
+ ...(hint ? { hint } : {}),
72
+ },
73
+ }));
74
+ assert(userAppKeys, 'could not find the app key');
75
+ const token = userAppKeys.appKey;
76
+ const results = await app.searchProducts({
77
+ token,
78
+ payload,
79
+ });
80
+ return res.json(results);
81
+ } catch (err) {
82
+ return next(err);
83
+ }
84
+ };
85
+
86
+ const bookingsAvailabilitySearch = plugins => async (req, res, next) => {
87
+ const {
88
+ params: { appKey, userId, hint },
89
+ body: payload,
90
+ } = req;
91
+ try {
92
+ const app = plugins.find(({ name }) => name === appKey);
93
+ // const app = load(appKey);
94
+ const userAppKeys = (await UserAppKey.findOne({
95
+ where: {
96
+ userId,
97
+ integrationId: appKey,
98
+ ...(hint ? { hint } : {}),
99
+ },
100
+ }));
101
+ assert(userAppKeys, 'could not find the app key');
102
+ const token = userAppKeys.appKey;
103
+ const results = await app.searchAvailability({
104
+ token,
105
+ payload,
106
+ });
107
+ return res.json(results);
108
+ } catch (err) {
109
+ return next(err);
110
+ }
111
+ };
112
+
113
+ const searchQuote = plugins => async (req, res, next) => {
114
+ const {
115
+ params: { appKey, userId, hint },
116
+ body: payload,
117
+ } = req;
118
+ try {
119
+ const app = plugins.find(({ name }) => name === appKey);
120
+ // const app = load(appKey);
121
+ const userAppKeys = (await UserAppKey.findOne({
122
+ where: {
123
+ userId,
124
+ integrationId: appKey,
125
+ ...(hint ? { hint } : {}),
126
+ },
127
+ }));
128
+ assert(userAppKeys, 'could not find the app key');
129
+ const token = userAppKeys.appKey;
130
+ assert(payload.id, 'the availability id is required');
131
+ const results = await app.searchQuote({
132
+ token,
133
+ payload,
134
+ });
135
+ return res.json(results);
136
+ } catch (err) {
137
+ return next(err);
138
+ }
139
+ };
140
+
141
+ const createBooking = plugins => async (req, res, next) => {
142
+ const {
143
+ params: { appKey, userId, hint },
144
+ body: payload,
145
+ } = req;
146
+ try {
147
+ const app = plugins.find(({ name }) => name === appKey);
148
+ // const app = load(appKey);
149
+ const userAppKeys = (await UserAppKey.findOne({
150
+ where: {
151
+ userId,
152
+ integrationId: appKey,
153
+ ...(hint ? { hint } : {}),
154
+ },
155
+ }));
156
+ assert(userAppKeys, 'could not find the app key');
157
+ const token = userAppKeys.appKey;
158
+ assert(payload.id, 'the quote id is required');
159
+ const results = await app.createBooking({
160
+ token,
161
+ payload,
162
+ });
163
+ return res.json(results);
164
+ } catch (err) {
165
+ return next(err);
166
+ }
167
+ };
168
+
60
169
  module.exports = plugins => ({
61
170
  bookingsSearch: bookingsSearch(plugins),
62
171
  bookingsCancel: bookingsCancel(plugins),
172
+ bookingsProductSearch: bookingsProductSearch(plugins),
173
+ bookingsAvailabilitySearch: bookingsAvailabilitySearch(plugins),
174
+ searchQuote: searchQuote(plugins),
175
+ createBooking: createBooking(plugins),
63
176
  });
@@ -0,0 +1,25 @@
1
+ {
2
+ "tags": {
3
+ "allowUnknownTags": true,
4
+ "dictionaries": ["jsdoc","closure"]
5
+ },
6
+ "source": {
7
+ "include": ["test"],
8
+ "includePattern": ".+\\.js(doc|x)?$",
9
+ "excludePattern": "(^|\\/|\\\\)_"
10
+ },
11
+ "plugins": [
12
+ "plugins/markdown"
13
+ ],
14
+ "templates": {
15
+ "cleverLinks": true,
16
+ "monospaceLinks": true
17
+ },
18
+ "opts": {
19
+ "destination": "docs",
20
+ "recurse": true,
21
+ "sort": false,
22
+ "readme": "README.md",
23
+ "template": "node_modules/minami"
24
+ }
25
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "ti2",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "Tourist Industry Exchange (TI2)",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "jest",
8
- "sequelize": "sequelize-cli"
8
+ "sequelize": "sequelize-cli",
9
+ "doc": "jsdoc -c jsdoc.conf.json"
9
10
  },
10
11
  "repository": {
11
12
  "type": "git",
@@ -39,6 +40,8 @@
39
40
  "eslint-config-airbnb-base": "^15.0.0",
40
41
  "eslint-plugin-import": "^2.25.4",
41
42
  "jest": "^27.4.7",
43
+ "jsdoc": "^3.6.10",
44
+ "minami": "git+https://github.com/MyNameReallySux/minami.git#sort-data",
42
45
  "supertest": "^6.2.2"
43
46
  }
44
47
  }
package/test/plugin.js CHANGED
@@ -1,17 +1,415 @@
1
- /* global jest */
1
+ /* global jest, expect */
2
+ const chance = require('chance').Chance();
2
3
 
3
4
  class Plugin {
5
+ /**
6
+ * **General** Methods:
7
+ * * [validateToken](#validateToken).
8
+ *
9
+ * **Content Methods**:
10
+ * * [getProfile](#getProfile)
11
+ * * [updateProfile](#updateProfile)
12
+ * * [createLocation](#createLocation)
13
+ * * [updateLocation](#updateLocation)
14
+ * * [getProducts](#getProducts)
15
+ * * [createProduct](#createProduct)
16
+ * * [updateProduct](#updateProduct)
17
+ *
18
+ * **Booking Methods**:
19
+ * * [searchBooking](#searchBooking)
20
+ * * [searchProducts](#searchProducts)
21
+ * * [searchAvailability](#searchAvailability)
22
+ * * [quoteAvailability](#quoteAvailability)
23
+ * * [createBooking](#createBooking)
24
+ * ---
25
+ *
26
+ * @param {Object} args - New / Overwriting attributes for the Plugin.
27
+ */
4
28
  constructor(params = {}) {
5
29
  Object.entries(params).forEach(([attr, value]) => {
6
30
  this[attr] = value;
7
31
  });
32
+ // mock implementations
33
+ this.validateToken = jest.fn(() => true);
34
+ this.getProfile = jest.fn(() => {});
35
+ this.updateProfile = jest.fn(() => {});
36
+ this.getProduct = jest.fn(() => ({ products: [] }));
37
+ this.getProducts = jest.fn(() => true);
38
+ this.createLocation = jest.fn(() => ({ locationId: chance.guid() }));
39
+ this.updateLocation = jest.fn(() => true);
40
+ this.searchBooking = jest.fn(() => ({ bookings: [] }));
41
+ this.searchProducts = jest.fn(() => ({ bookings: [], products: [] }));
42
+ this.searchAvailability = jest.fn(({
43
+ token,
44
+ payload: {
45
+ travelDateStart,
46
+ travelDateEnd,
47
+ dateFormat,
48
+ occupancies,
49
+ },
50
+ }) => {
51
+ expect(token).toBeTruthy();
52
+ expect(travelDateStart).toBeTruthy();
53
+ expect(travelDateEnd).toBeTruthy();
54
+ expect(dateFormat).toBeTruthy();
55
+ expect(Array.isArray(occupancies)).toBeTruthy();
56
+ return { availability: [{ id: chance.guid() }] };
57
+ });
58
+
59
+ this.searchQuote = jest.fn(() => ({ quote: [{ id: chance.guid() }] }));
60
+ this.createBooking = jest.fn(() => {});
61
+ }
62
+
63
+ /**
64
+ * Check the vailidity of an integration token. *part of **General** Methods*
65
+ * @async
66
+ * @param {Object} args - Availability search arguments.
67
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
68
+ * @returns {Boolean} - The token vailidity True/False
69
+ */
70
+ validateToken() {
71
+ return Boolean(this);
72
+ }
73
+ /**
74
+ * GPS Location Object
75
+ * @typedef {Object} GPSLocation
76
+ * @property {String} lat - GPS latitude
77
+ * @property {String} lng - GPS Longitude
78
+ */
79
+
80
+ /**
81
+ * Address Object
82
+ * @typedef {Object} Address
83
+ * @property {string} addressOne - Street Address first line of text
84
+ * @property {string} addressTwo - Street Address second line of text
85
+ * @property {string} city - City
86
+ * @property {string} state - State
87
+ * @property {string} zipCode - Zip/Postal Code
88
+ * @property {GPSLocation} loc -Location
89
+ */
90
+
91
+ /**
92
+ * Profile Object
93
+ * @typedef {Object} Profile
94
+ * @property {string} companyName
95
+ * @property {Address} address
96
+ */
97
+
98
+ /**
99
+ * Returns the profile details of the account.
100
+ * @category Methods
101
+ * @subcategory Content
102
+ * Returns the profile details of the account.
103
+ * @async
104
+ * @param {Object} args - Availability search arguments.
105
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
106
+ * @returns {Profile} - The profile details.
107
+ */
108
+ getProfile() {
109
+ return {};
110
+ }
111
+
112
+ /**
113
+ * Updates profile information
114
+ * @async
115
+ * @param {Object} args - Availability search arguments.
116
+ * @param {Profile} args.payload - Updated profile information
117
+ * @returns {boolean} - True/False success status.
118
+ */
119
+ updateProfile() {
120
+ return {};
8
121
  }
9
122
 
10
- validateToken = jest.fn();
123
+ /**
124
+ * Location Object
125
+ * @typedef {Object} Location
126
+ * @property {string} telephone
127
+ * @property {Address} address
128
+ */
129
+
130
+ /**
131
+ * Create a Location
132
+ * @async
133
+ * @param {Object} args - Availability search arguments.
134
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
135
+ * @param {Location} args.payload - The new location details.
136
+ * @returns {Object} retVal - An object containing return values.
137
+ * @returns {string} retVal.locationId - The new location unique identifier.
138
+ */
139
+ createLocation() {
140
+ return { locationId: chance.guid() };
141
+ }
142
+
143
+ /**
144
+ * Update a Location
145
+ * @async
146
+ * @param {Object} args - Availability search arguments.
147
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
148
+ * @param {string} args.locationId - The unique identifier of the location to update.
149
+ * @param {Location} args.payload - The new location details.
150
+ * @returns {boolean} - True/False success status.
151
+ */
152
+ updateLocation() {
153
+ return true;
154
+ }
155
+ /**
156
+ * Media Object
157
+ * @typedef {Object} Media - Media Details
158
+ * @property {string} mediaType - MimeType of media
159
+ * @property {string} url - URL of media
160
+ */
11
161
 
12
- getProduct = jest.fn();
162
+ /**
163
+ * Media Collection
164
+ * @typedef {Object} MediaCollection - Media Collection, grouped by type
165
+ * @property {Array.Media} image
166
+ * @property {Array.Media} video
167
+ */
168
+
169
+ /**
170
+ * Room Object
171
+ * @typedef {Object} RoomInfo - Room details object
172
+ * @property {number} count - Number of rooms
173
+ * @property {string} bedType - Bedding type
174
+ * @property {string} other - Exta Information
175
+ */
176
+
177
+ /**
178
+ * Value and UUnit spec
179
+ * @typedef {Object} ValueUnit - ValueDetails fields
180
+ * @property {string} unit - A unit specification
181
+ * @property {number} value - The number of units
182
+ */
183
+
184
+ /**
185
+ * Get the product details related to a location
186
+ * @async
187
+ * @param {Object} args - Availability search arguments.
188
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
189
+ * @param {Object} args.locationId - A location unique identifier for the matching product.
190
+ * @param {Object} args.productId - The product unique identifier.
191
+ * @returns {Object} retVal - A return object.
192
+ * @returns {Product} retVal.product - Details of the matching product.
193
+ */
194
+ /**
195
+ * ProductInfo type
196
+ * @typedef ProductInfo
197
+ * @property {string} include
198
+ * @property {string} exclude
199
+ * @property {string} whatToBring
200
+ */
201
+ /**
202
+ * bedsAndSofas type
203
+ * @typedef BedsAndSofas
204
+ * @property {boolean} isApartment
205
+ * @property {numeric} count
206
+ * @property {string} other
207
+ */
208
+ /**
209
+ * Product Object
210
+ * @typedef {Object} Product - Product information Object
211
+ * @property {string} productId - A unique identifier for a product
212
+ * @property {string} productName
213
+ * @property {('accommodation'|'non-accommodation')} productType
214
+ * @property {string} productCode
215
+ * @property {string} description
216
+ * @property {string} notes
217
+ * @property {string} coverImageUrl
218
+ * @property {boolean} interconnectingRooms
219
+ * @property {string} amenities
220
+ * @property {ValueUnit} roomCountInfo
221
+ * @property {MediaCollection} media
222
+ * @property {ValueUnit} roomSizeInfo
223
+ * @property {ValueUnit} tourDuration
224
+ * @property {number} totalMaxPassengers
225
+ * @property {number} totalMinPassengers
226
+ * @property {ValueUnit} totalDuration
227
+ * @property {boolean} tourDurationDoesNotApply
228
+ * @property {ProductInfo} productInfo
229
+ * @property {BedsAndSofas} bedsAndSofas
230
+ */
231
+ getProduct() {
232
+ return {};
233
+ }
234
+
235
+ /**
236
+ * Get the list of products for a given location
237
+ * @async
238
+ * @param {Object} args - Availability search arguments.
239
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
240
+ * @param {Object} args.locationId - A location unique identifier to get the list of products.
241
+ * @returns {Object} retVal - An object containing return values.
242
+ * @returns {Array.Product} retVal.products - An array of matching products.
243
+ */
244
+ getProducts() {
245
+ return { products: [] };
246
+ }
247
+
248
+ /**
249
+ * Create a Product
250
+ * @async
251
+ * @param {Object} args - Availability search arguments.
252
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
253
+ * @param {string} args.locationId - The unique identifier of the location that will contain the product.
254
+ * @param {Product} args.payload - The new product details.
255
+ * @returns {Object} retVal - An object containing return values.
256
+ * @returns {string} retVal.locationId - The new location unique identifier.
257
+ */
258
+ createProduct() {
259
+ return { locationId: chance.guid(), productId: chance.guid() };
260
+ }
261
+
262
+ /**
263
+ * Update a Product
264
+ * @async
265
+ * @param {Object} args - Availability search arguments.
266
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
267
+ * @param {string} args.locationId - The unique identifier of the location that contains the product.
268
+ * @param {string} args.productId - The unique identifier of the product to update.
269
+ * @param {Product} args.payload - The new location details.
270
+ * @returns {boolean} - True/False success status.
271
+ */
272
+ updateProduct() {
273
+ return true;
274
+ }
13
275
 
14
276
  searchHotelBooking = jest.fn(() => ({ bookings: [] }));
277
+
278
+ /**
279
+ * Booking Object
280
+ * @typedef {Object} Booking
281
+ * @property {string} id Booking unique identifier.
282
+ * @property {Cancelled|Active|Pending} status The currrent booking status.
283
+ * @property {Holder} holder Booking holder information.
284
+ * @property {string} telephone Contact telephone.
285
+ * @property {string} supplierId Supplier of the Booking
286
+ * @property {string} hotelId A HotelId if the booking is for an accommodation.
287
+ * @property {string} hotelName A Hotel Name if the booking is for an accommodation.
288
+ * @property {BookingRoom[]} rooms Booking Room details.
289
+ * @property {string} start CheckIn.
290
+ * @property {string} end CheckOut.
291
+ * @property {string} bookingDate Date when the booking was created.
292
+ * @property {numeric} price Total price for the booking.
293
+ * @property {CancelPolicy} cancelPolicy Cancellation policies for the booking.
294
+ */
295
+ /**
296
+ * Booking Room
297
+ * @typedef {Object} BookingRoom
298
+ * @property {string} roomId
299
+ * @property {string} description
300
+ * @property {numeric} price
301
+ */
302
+
303
+ /**
304
+ * Cancel Policy
305
+ * @typedef {Object} CancelPolicy
306
+ * @property {boolean} refundable
307
+ * @property {string} cancelPenalties
308
+ */
309
+
310
+ /**
311
+ * Search for bookings with a criteria.
312
+ * @async
313
+ * @param {Object} args - Booking search arguments.
314
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
315
+ * @param {Object} args.payload - Search criteria object.
316
+ * @param {string} args.payload.bookingId - a Booking Id.
317
+ * @param {string} args.payload.dateFormat - Date format sent, using [momentjs formatting](https://momentjs.com/docs/#/parsing/string-format/).
318
+ * @param {string} args.payload.purchaseDateStart - Date of purchase start.
319
+ * @param {string} args.payload.purchaseDateEnd - Date of purchase end.
320
+ * @param {string} args.payload.travelDateStart - Date of travel start.
321
+ * @param {string} args.payload.travelDateEnd - Date of travel end.
322
+ * @param {Holder} args.payload.holder - Search for a particular holder.
323
+ * @returns {object} retVal - the return object.
324
+ * @returns {Booking[]} retVal.bookings - Array of Bookings matchin the criteria.
325
+ */
326
+ searchBooking() {
327
+ }
328
+
329
+ /**
330
+ * Search for bookable products
331
+ * @async
332
+ * @param {Object} args - Product search arguments.
333
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
334
+ * @param {Object} args.payload - Search criteria object.
335
+ * @returns {object} retVal - the return object.
336
+ * @returns {Hotel[]} retVal.hotels - Array of bookable hotels.
337
+ * @returns {Product[]} retVal.products - Array of bookable activities.
338
+ * @todo Define search criteria and Hotel/Product spec.
339
+ */
340
+ searchProducts() {
341
+ }
342
+
343
+ /**
344
+ * Occupancy Object
345
+ * @typedef {Object} Occupancy
346
+ * @property {Pax[]} paxes
347
+ */
348
+ /**
349
+ * A Pax item with an age spec.
350
+ * @typedef {Object} Pax
351
+ * @property {number} age Age in years of the pax when the startDate starts on local timezone.
352
+ */
353
+ /**
354
+ * An availability return spec.
355
+ * @typedef {Object} Availability
356
+ * @property {string} id An identifier that can be used to retrieve a quote.
357
+ */
358
+ /**
359
+ * A booking holder spec.
360
+ * @typedef {Object} Holder
361
+ * @property {string} name The booking's holder first name (s).
362
+ * @property {string} surname Surname / Last Name(s) for the booking holder.
363
+ * @property {string} email The booking's holder email.
364
+ */
365
+ /**
366
+ * Search for availability of products winthin dates of travel.
367
+ * @async
368
+ * @param {Object} args - Availability search arguments.
369
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
370
+ * @param {Object} args.payload - Search spect object.
371
+ * @param {string} args.payload.dateFormat - Date format sent, using [momentjs formatting](https://momentjs.com/docs/#/parsing/string-format/).
372
+ * @param {string} args.payload.travelDateStart - Date of travel start.
373
+ * @param {string} args.payload.travelDateEnd - Date of travel end.
374
+ * @param {Occupancy[]} args.payload.occupancies - Occupancy detail.
375
+ * @returns {object} retVal - the return object
376
+ * @returns {Availability[]} retVal.availability - Array of availability objects
377
+ */
378
+ searchAvailability() {
379
+ // return (args);
380
+ }
381
+
382
+ /**
383
+ * A quote return spec
384
+ * @typedef {Object} Quote
385
+ * @property {string} id An identifier that can be used to create a booking.
386
+ */
387
+ /**
388
+ * Quote an availability.
389
+ * @async
390
+ * @param {Object} args - Availability quote arguments.
391
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
392
+ * @param {Object} args.payload - Search spect object.
393
+ * @param {string} args.payload.id - An availability Id.
394
+ * @returns {object} retVal - the return object.
395
+ * @returns {Quote} retVal.quote - A Quote Object instance.
396
+ */
397
+ searchQuote() {
398
+ // return (args);
399
+ }
400
+
401
+ /**
402
+ * Create a Booking
403
+ * @async
404
+ * @param {Object} args - Booking arguments.
405
+ * @param {Object} args.token - A token definition, it's content varies between integrations.
406
+ * @param {Object} args.payload - Search spect object.
407
+ * @param {string} args.payload.id - A quote id.
408
+ * @param {Holder} args.payload.holder - Contact information of the booking holder.
409
+ * @returns {object} retVal - the return object.
410
+ * @returns {Booking} retVal.booking - A Booking object.
411
+ */
412
+ createBooking() {}
15
413
  }
16
414
 
17
415
  module.exports = Plugin;