@duffel/components 2.7.19 → 3.0.0-canary
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/.circleci/config.yml +67 -0
- package/.eslintrc.js +34 -0
- package/.github/renovate.json +16 -0
- package/.github/workflows/autoapprove.yml +18 -0
- package/.github/workflows/release.yml +86 -0
- package/.husky/post-commit +4 -0
- package/.husky/pre-commit +4 -0
- package/.nvmrc +1 -0
- package/.prettierignore +2 -0
- package/.storybook/Storyshots.test.js +3 -0
- package/.storybook/__snapshots__/Storyshots.test.js.snap +48133 -0
- package/.storybook/main.ts +33 -0
- package/.storybook/preview.tsx +28 -0
- package/.tool-versions +1 -0
- package/README.md +129 -16
- package/__mocks__/styleMock.js +6 -0
- package/babel.config.js +20 -0
- package/commitlint.config.js +4 -0
- package/config/esbuild.base.config.js +14 -0
- package/config/esbuild.cdn.config.js +50 -0
- package/config/esbuild.dev.config.js +45 -0
- package/config/esbuild.react.config.js +42 -0
- package/jest.config.ts +14 -0
- package/package.json +123 -192
- package/react-dist/components/AnimatedLoaderEllipsis.d.ts +2 -0
- package/react-dist/components/Button.d.ts +23 -0
- package/react-dist/components/Card.d.ts +14 -0
- package/react-dist/components/Counter.d.ts +10 -0
- package/react-dist/components/DuffelAncillaries.d.ts +3 -0
- package/react-dist/components/DuffelAncillariesCustomElement.d.ts +13 -0
- package/react-dist/components/ErrorBoundary.d.ts +13 -0
- package/react-dist/components/FetchOfferErrorState.d.ts +5 -0
- package/react-dist/components/Icon.d.ts +44 -0
- package/react-dist/components/IconButton.d.ts +16 -0
- package/react-dist/components/Modal.d.ts +11 -0
- package/react-dist/components/NonIdealState.d.ts +4 -0
- package/react-dist/components/Stamp.d.ts +7 -0
- package/react-dist/components/Tabs.d.ts +16 -0
- package/react-dist/components/bags/BaggageSelectionCard.d.ts +11 -0
- package/react-dist/components/bags/BaggageSelectionController.d.ts +13 -0
- package/react-dist/components/bags/BaggageSelectionModal.d.ts +11 -0
- package/react-dist/components/bags/BaggageSelectionModalBody.d.ts +11 -0
- package/react-dist/components/bags/BaggageSelectionModalBodyPassenger.d.ts +13 -0
- package/react-dist/components/bags/BaggageSelectionModalFooter.d.ts +14 -0
- package/react-dist/components/bags/BaggageSelectionModalHeader.d.ts +9 -0
- package/react-dist/components/bags/IncludedBaggageBanner.d.ts +7 -0
- package/react-dist/components/cancel_for_any_reason/CfarSelectionCard.d.ts +10 -0
- package/react-dist/components/cancel_for_any_reason/CfarSelectionModal.d.ts +11 -0
- package/react-dist/components/cancel_for_any_reason/CfarSelectionModalBody.d.ts +7 -0
- package/react-dist/components/cancel_for_any_reason/CfarSelectionModalBodyListItem.d.ts +4 -0
- package/react-dist/components/cancel_for_any_reason/CfarSelectionModalFooter.d.ts +11 -0
- package/react-dist/components/cancel_for_any_reason/CfarSelectionModalHeader.d.ts +2 -0
- package/react-dist/components/seats/Amenity.d.ts +6 -0
- package/react-dist/components/seats/DeckSelect.d.ts +15 -0
- package/react-dist/components/seats/Element.d.ts +15 -0
- package/react-dist/components/seats/EmptyElement.d.ts +2 -0
- package/react-dist/components/seats/ExitElement.d.ts +6 -0
- package/react-dist/components/seats/Legend.d.ts +12 -0
- package/react-dist/components/seats/Row.d.ts +13 -0
- package/react-dist/components/seats/RowSection.d.ts +17 -0
- package/react-dist/components/seats/SeatElement.d.ts +13 -0
- package/react-dist/components/seats/SeatInfo.d.ts +7 -0
- package/react-dist/components/seats/SeatMap.d.ts +12 -0
- package/react-dist/components/seats/SeatMapUnavailable.d.ts +2 -0
- package/react-dist/components/seats/SeatSelectionCard.d.ts +13 -0
- package/react-dist/components/seats/SeatSelectionModal.d.ts +13 -0
- package/react-dist/components/seats/SeatSelectionModalBody.d.ts +4 -0
- package/react-dist/components/seats/SeatSelectionModalFooter.d.ts +16 -0
- package/react-dist/components/seats/SeatSelectionModalHeader.d.ts +10 -0
- package/react-dist/components/seats/SeatUnavailable.d.ts +5 -0
- package/react-dist/index.d.ts +8 -0
- package/react-dist/index.js +36 -0
- package/react-dist/index.js.map +7 -0
- package/react-dist/lib/captureErrorInSentry.d.ts +1 -0
- package/react-dist/lib/compileCreateOrderPayload.d.ts +14 -0
- package/react-dist/lib/createPriceFormatters.d.ts +12 -0
- package/react-dist/lib/fetchFromDuffelAPI.d.ts +1 -0
- package/react-dist/lib/fetchFromFixtures.d.ts +4 -0
- package/react-dist/lib/formatAvailableServices.d.ts +12 -0
- package/react-dist/lib/formatDate.d.ts +2 -0
- package/react-dist/lib/formatSeatMaps.d.ts +4 -0
- package/react-dist/lib/getBaggageServiceDescription.d.ts +2 -0
- package/react-dist/lib/getCabinsForSegmentAndDeck.d.ts +2 -0
- package/react-dist/lib/getCurrencyForSeatMaps.d.ts +10 -0
- package/react-dist/lib/getCurrencyForServices.d.ts +11 -0
- package/react-dist/lib/getFirstSeatElementMatchingCriteria.d.ts +3 -0
- package/react-dist/lib/getPassengerBySegmentList.d.ts +6 -0
- package/react-dist/lib/getPassengerInitials.d.ts +1 -0
- package/react-dist/lib/getPassengerMapById.d.ts +3 -0
- package/react-dist/lib/getPassengerName.d.ts +3 -0
- package/react-dist/lib/getRowNumber.d.ts +2 -0
- package/react-dist/lib/getSegmentList.d.ts +2 -0
- package/react-dist/lib/getServicePriceMapById.d.ts +3 -0
- package/react-dist/lib/getSymbols.d.ts +2 -0
- package/react-dist/lib/getTotalAmountForServices.d.ts +6 -0
- package/react-dist/lib/getTotalQuantity.d.ts +2 -0
- package/react-dist/lib/hasService.d.ts +2 -0
- package/react-dist/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.d.ts +3 -0
- package/react-dist/lib/hasWings.d.ts +2 -0
- package/react-dist/lib/isBaggageService.d.ts +2 -0
- package/react-dist/lib/isCancelForAnyReasonService.d.ts +2 -0
- package/react-dist/lib/isFixtureOfferId.d.ts +2 -0
- package/react-dist/lib/isPayloadComplete.d.ts +2 -0
- package/react-dist/lib/isSeatElement.d.ts +2 -0
- package/react-dist/lib/logging.d.ts +53 -0
- package/react-dist/lib/moneyStringFormatter.d.ts +8 -0
- package/react-dist/lib/offerIsExpired.d.ts +2 -0
- package/react-dist/lib/retrieveOffer.d.ts +2 -0
- package/react-dist/lib/retrieveOfferFromDuffelAPI.d.ts +1 -0
- package/react-dist/lib/retrieveSeatMaps.d.ts +2 -0
- package/react-dist/lib/retrieveSeatMapsFromDuffelAPI.d.ts +1 -0
- package/react-dist/lib/setBodyScrollability.d.ts +1 -0
- package/react-dist/lib/validateProps.d.ts +7 -0
- package/react-dist/lib/withPlural.d.ts +1 -0
- package/react-dist/types/Aircraft.d.ts +14 -0
- package/react-dist/types/Airline.d.ts +14 -0
- package/react-dist/types/Airport.d.ts +44 -0
- package/react-dist/types/City.d.ts +18 -0
- package/react-dist/types/CreateOrderPayload.d.ts +72 -0
- package/react-dist/types/CurrencyConversion.d.ts +10 -0
- package/react-dist/types/DuffelAncillariesProps.d.ts +70 -0
- package/react-dist/types/Offer.d.ts +711 -0
- package/react-dist/types/Order.d.ts +8 -0
- package/react-dist/types/Place.d.ts +8 -0
- package/react-dist/types/SeatMap.d.ts +190 -0
- package/react-dist/types/index.d.ts +11 -0
- package/scripts/generate-fixture.ts +195 -0
- package/scripts/upload-to-cdn.sh +39 -0
- package/scripts.tsconfig.json +11 -0
- package/src/components/AnimatedLoaderEllipsis.tsx +5 -0
- package/src/components/Button.tsx +62 -0
- package/src/components/Card.tsx +126 -0
- package/src/components/Counter.tsx +40 -0
- package/src/components/DuffelAncillaries.tsx +346 -0
- package/src/components/DuffelAncillariesCustomElement.tsx +124 -0
- package/src/components/ErrorBoundary.tsx +54 -0
- package/src/components/FetchOfferErrorState.tsx +35 -0
- package/src/components/Icon.tsx +151 -0
- package/src/components/IconButton.tsx +42 -0
- package/src/components/Modal.tsx +36 -0
- package/src/components/NonIdealState.tsx +28 -0
- package/src/components/Stamp.tsx +29 -0
- package/src/components/Tabs.tsx +36 -0
- package/src/components/bags/BaggageSelectionCard.tsx +96 -0
- package/src/components/bags/BaggageSelectionController.tsx +88 -0
- package/src/components/bags/BaggageSelectionModal.tsx +81 -0
- package/src/components/bags/BaggageSelectionModalBody.tsx +60 -0
- package/src/components/bags/BaggageSelectionModalBodyPassenger.tsx +122 -0
- package/src/components/bags/BaggageSelectionModalFooter.tsx +81 -0
- package/src/components/bags/BaggageSelectionModalHeader.tsx +76 -0
- package/src/components/bags/IncludedBaggageBanner.tsx +51 -0
- package/src/components/cancel_for_any_reason/CfarSelectionCard.tsx +90 -0
- package/src/components/cancel_for_any_reason/CfarSelectionModal.tsx +63 -0
- package/src/components/cancel_for_any_reason/CfarSelectionModalBody.tsx +56 -0
- package/src/components/cancel_for_any_reason/CfarSelectionModalBodyListItem.tsx +11 -0
- package/src/components/cancel_for_any_reason/CfarSelectionModalFooter.tsx +74 -0
- package/src/components/cancel_for_any_reason/CfarSelectionModalHeader.tsx +9 -0
- package/src/components/seats/Amenity.tsx +21 -0
- package/src/components/seats/DeckSelect.tsx +27 -0
- package/src/components/seats/Element.tsx +52 -0
- package/src/components/seats/EmptyElement.tsx +5 -0
- package/src/components/seats/ExitElement.tsx +17 -0
- package/src/components/seats/Legend.tsx +60 -0
- package/src/components/seats/Row.tsx +47 -0
- package/src/components/seats/RowSection.tsx +75 -0
- package/src/components/seats/SeatElement.tsx +120 -0
- package/src/components/seats/SeatInfo.tsx +32 -0
- package/src/components/seats/SeatMap.tsx +81 -0
- package/src/components/seats/SeatMapUnavailable.tsx +21 -0
- package/src/components/seats/SeatSelectionCard.tsx +103 -0
- package/src/components/seats/SeatSelectionModal.tsx +142 -0
- package/src/components/seats/SeatSelectionModalBody.tsx +13 -0
- package/src/components/seats/SeatSelectionModalFooter.tsx +82 -0
- package/src/components/seats/SeatSelectionModalHeader.tsx +87 -0
- package/src/components/seats/SeatUnavailable.tsx +14 -0
- package/src/examples/client-side/index.html +57 -0
- package/src/examples/full-stack/index.html +48 -0
- package/src/examples/full-stack/server.mjs +157 -0
- package/src/examples/just-typescript/README.md +37 -0
- package/src/examples/just-typescript/package.json +16 -0
- package/src/examples/just-typescript/src/index.html +23 -0
- package/src/examples/just-typescript/src/index.ts +35 -0
- package/src/examples/just-typescript/yarn.lock +154 -0
- package/src/examples/react-app/README.md +37 -0
- package/src/examples/react-app/package.json +20 -0
- package/src/examples/react-app/src/index.html +19 -0
- package/src/examples/react-app/src/index.tsx +43 -0
- package/src/examples/react-app/yarn.lock +219 -0
- package/src/fixtures/offers/off_0000AUde3KwTztSRK1cznH.json +497 -0
- package/src/fixtures/offers/off_0000AVx4lUFFKW8PsPeQeQ.json +307 -0
- package/src/fixtures/offers/off_1.json +497 -0
- package/src/fixtures/passengers/mock_passengers.ts +26 -0
- package/src/fixtures/seat-maps/off_0000AUde3KwTztSRK1cznH.json +6852 -0
- package/src/fixtures/seat-maps/off_0000AVx4lUFFKW8PsPeQeQ.json +1 -0
- package/src/fixtures/seat-maps/off_1.json +6852 -0
- package/src/index.ts +8 -0
- package/src/lib/captureErrorInSentry.ts +60 -0
- package/src/lib/compileCreateOrderPayload.ts +63 -0
- package/src/lib/createPriceFormatters.ts +73 -0
- package/src/lib/fetchFromDuffelAPI.ts +24 -0
- package/src/lib/fetchFromFixtures.ts +18 -0
- package/src/lib/formatAvailableServices.ts +91 -0
- package/src/lib/formatDate.ts +21 -0
- package/src/lib/formatSeatMaps.ts +81 -0
- package/src/lib/getBaggageServiceDescription.ts +44 -0
- package/src/lib/getCabinsForSegmentAndDeck.ts +4 -0
- package/src/lib/getCurrencyForSeatMaps.ts +22 -0
- package/src/lib/getCurrencyForServices.ts +24 -0
- package/src/lib/getFirstSeatElementMatchingCriteria.ts +22 -0
- package/src/lib/getPassengerBySegmentList.ts +10 -0
- package/src/lib/getPassengerInitials.ts +6 -0
- package/src/lib/getPassengerMapById.ts +17 -0
- package/src/lib/getPassengerName.ts +37 -0
- package/src/lib/getRowNumber.ts +16 -0
- package/src/lib/getSegmentList.ts +7 -0
- package/src/lib/getServicePriceMapById.ts +20 -0
- package/src/lib/getSymbols.ts +22 -0
- package/src/lib/getTotalAmountForServices.ts +72 -0
- package/src/lib/getTotalQuantity.ts +5 -0
- package/src/lib/hasService.ts +24 -0
- package/src/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.ts +35 -0
- package/src/lib/hasWings.ts +8 -0
- package/src/lib/isBaggageService.ts +8 -0
- package/src/lib/isCancelForAnyReasonService.ts +9 -0
- package/src/lib/isFixtureOfferId.ts +4 -0
- package/src/lib/isPayloadComplete.ts +11 -0
- package/src/lib/isSeatElement.ts +10 -0
- package/src/lib/logging.ts +100 -0
- package/src/lib/moneyStringFormatter.ts +34 -0
- package/src/lib/offerIsExpired.ts +5 -0
- package/src/lib/retrieveOffer.ts +49 -0
- package/src/lib/retrieveOfferFromDuffelAPI.ts +13 -0
- package/src/lib/retrieveSeatMaps.ts +50 -0
- package/src/lib/retrieveSeatMapsFromDuffelAPI.ts +13 -0
- package/src/lib/setBodyScrollability.ts +7 -0
- package/src/lib/validateProps.ts +37 -0
- package/src/lib/withPlural.ts +8 -0
- package/src/stories/BaggageSelectionModalHeader.stories.tsx +21 -0
- package/src/stories/Button.stories.tsx +60 -0
- package/src/stories/DuffelAncillaries.stories.tsx +126 -0
- package/src/stories/Icon.stories.tsx +34 -0
- package/src/stories/IconButton.stories.tsx +25 -0
- package/src/styles/colors.css +22 -0
- package/src/styles/components/Amenity.css +23 -0
- package/src/styles/components/BaggageDisplay.css +25 -0
- package/src/styles/components/Button.css +161 -0
- package/src/styles/components/Card.css +52 -0
- package/src/styles/components/CfarSelectionModal.css +34 -0
- package/src/styles/components/Counter.css +18 -0
- package/src/styles/components/IconButton.css +63 -0
- package/src/styles/components/Legend.css +58 -0
- package/src/styles/components/Loader.css +37 -0
- package/src/styles/components/LoadingState.css +81 -0
- package/src/styles/components/Modal.css +83 -0
- package/src/styles/components/PassengerSelect.css +93 -0
- package/src/styles/components/PassengersLayout.css +90 -0
- package/src/styles/components/Row.css +70 -0
- package/src/styles/components/Seat.css +57 -0
- package/src/styles/components/SeatInfo.css +61 -0
- package/src/styles/components/SeatMap.css +24 -0
- package/src/styles/components/SeatSelect.css +92 -0
- package/src/styles/components/Segment.css +17 -0
- package/src/styles/components/SelectionSegment.css +10 -0
- package/src/styles/components/Summary.css +70 -0
- package/src/styles/components/Tabs.css +49 -0
- package/src/styles/flex.css +5 -0
- package/src/styles/font-families.css +47 -0
- package/src/styles/global.css +50 -0
- package/src/styles/margin.css +3 -0
- package/src/styles/spacing.css +18 -0
- package/src/styles/transitions.css +3 -0
- package/src/styles/typography.css +13 -0
- package/src/tests/components/DuffelAncillaries.test.tsx +342 -0
- package/src/tests/lib/createPriceFormatters.test.tsx +152 -0
- package/src/tests/lib/formatAvailableServices.test.tsx +79 -0
- package/src/tests/lib/formatSeatMaps.test.tsx +49 -0
- package/src/tests/lib/getCurrencyForServices.test.tsx +44 -0
- package/src/tests/lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected.test.ts +86 -0
- package/src/tests/lib/logging.test.tsx +32 -0
- package/src/tests/lib/moneyStringFormatter.test.tsx +12 -0
- package/src/tests/lib/validateProps.test.tsx +57 -0
- package/src/types/Aircraft.ts +16 -0
- package/src/types/Airline.ts +16 -0
- package/src/types/Airport.ts +54 -0
- package/src/types/City.ts +21 -0
- package/src/types/CreateOrderPayload.ts +99 -0
- package/src/types/CurrencyConversion.ts +10 -0
- package/src/types/DuffelAncillariesProps.ts +108 -0
- package/src/types/Offer.ts +851 -0
- package/src/types/Order.ts +6 -0
- package/src/types/Place.ts +6 -0
- package/src/types/SeatMap.ts +231 -0
- package/src/types/index.ts +11 -0
- package/tsconfig.json +52 -0
- package/LICENSE +0 -21
- package/dist/AdditionalBaggage.esm.js +0 -1
- package/dist/AdditionalBaggage.js +0 -1
- package/dist/AdditionalBaggage.min.css +0 -408
- package/dist/AdditionalBaggage.umd.min.js +0 -2
- package/dist/AdditionalBaggage.umd.min.js.LICENSE.txt +0 -60
- package/dist/AdditionalBaggageSelection.esm.js +0 -1
- package/dist/AdditionalBaggageSelection.js +0 -1
- package/dist/AdditionalBaggageSelection.min.css +0 -744
- package/dist/AdditionalBaggageSelection.umd.min.js +0 -2
- package/dist/AdditionalBaggageSelection.umd.min.js.LICENSE.txt +0 -93
- package/dist/CardPayment.esm.js +0 -2
- package/dist/CardPayment.esm.js.LICENSE.txt +0 -6
- package/dist/CardPayment.js +0 -2
- package/dist/CardPayment.js.LICENSE.txt +0 -6
- package/dist/CardPayment.min.css +0 -233
- package/dist/CardPayment.umd.min.js +0 -2
- package/dist/CardPayment.umd.min.js.LICENSE.txt +0 -61
- package/dist/SeatSelection.esm.js +0 -1
- package/dist/SeatSelection.js +0 -1
- package/dist/SeatSelection.min.css +0 -1127
- package/dist/SeatSelection.umd.min.js +0 -2
- package/dist/SeatSelection.umd.min.js.LICENSE.txt +0 -60
- package/dist/duffel-components.d.ts +0 -1614
- package/dist/duffel-components.esm.js +0 -2
- package/dist/duffel-components.esm.js.LICENSE.txt +0 -6
- package/dist/duffel-components.js +0 -2
- package/dist/duffel-components.js.LICENSE.txt +0 -6
- package/dist/duffel-components.min.css +0 -1280
- package/dist/duffel-components.umd.min.js +0 -2
- package/dist/duffel-components.umd.min.js.LICENSE.txt +0 -102
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { createPriceFormatters } from "@lib/createPriceFormatters";
|
|
2
|
+
|
|
3
|
+
describe("createPriceFormatters", () => {
|
|
4
|
+
it("should return an empty object if neither markup nor priceFormatters are supplied", () => {
|
|
5
|
+
const formatters = createPriceFormatters(undefined, undefined);
|
|
6
|
+
expect(formatters).toEqual({});
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("should return an empty object if both markup and priceFormatters are empty", () => {
|
|
10
|
+
const formatters = createPriceFormatters({}, {});
|
|
11
|
+
expect(formatters).toEqual({});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should convert the markup into a price formatter for bags", () => {
|
|
15
|
+
const markup = {
|
|
16
|
+
rate: 0.1,
|
|
17
|
+
amount: 100,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const formatters = createPriceFormatters({ bags: markup }, {});
|
|
21
|
+
|
|
22
|
+
// Force unwrap the price formatter and use 'any' to avoid type errors.
|
|
23
|
+
// We know this is safe because we created the function right above this.
|
|
24
|
+
const formattedPrice = formatters.bags!(100, "GBP", {} as any);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Note: This test is also confirming that we apply the rate, followed by
|
|
28
|
+
* the amount, rather than the other way around.
|
|
29
|
+
* For example, if we applied the amount first, the result would be 220.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const originalPrice = 100;
|
|
33
|
+
const expectedFormattedPrice =
|
|
34
|
+
originalPrice * (1 + markup.rate) + markup.amount;
|
|
35
|
+
|
|
36
|
+
expect(formattedPrice).toEqual({
|
|
37
|
+
amount: expectedFormattedPrice,
|
|
38
|
+
currency: "GBP",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(formatters.seats).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should convert the markup into a price formatter for seats", () => {
|
|
45
|
+
const markup = {
|
|
46
|
+
rate: 0.1,
|
|
47
|
+
amount: 100,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const formatters = createPriceFormatters({ seats: markup }, {});
|
|
51
|
+
|
|
52
|
+
// Force unwrap the price formatter and use 'any' to avoid type errors.
|
|
53
|
+
// We know this is safe because we created the function right above this.
|
|
54
|
+
const formattedPrice = formatters.seats!(100, "GBP", {} as any);
|
|
55
|
+
|
|
56
|
+
const originalPrice = 100;
|
|
57
|
+
const expectedFormattedPrice =
|
|
58
|
+
originalPrice * (1 + markup.rate) + markup.amount;
|
|
59
|
+
|
|
60
|
+
expect(formattedPrice).toEqual({
|
|
61
|
+
amount: expectedFormattedPrice,
|
|
62
|
+
currency: "GBP",
|
|
63
|
+
});
|
|
64
|
+
expect(formatters.bags).toBeUndefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should convert the markup into a price formatter for both bags and seats", () => {
|
|
68
|
+
const formatters = createPriceFormatters(
|
|
69
|
+
{
|
|
70
|
+
bags: {
|
|
71
|
+
rate: 0.5,
|
|
72
|
+
amount: 1,
|
|
73
|
+
},
|
|
74
|
+
seats: {
|
|
75
|
+
rate: 0.1,
|
|
76
|
+
amount: 100,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
undefined
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Force unwrap the price formatter and use 'any' to avoid type errors.
|
|
83
|
+
// We know this is safe because we created the function right above this.
|
|
84
|
+
const formattedPriceForBags = formatters.bags!(100, "GBP", {} as any);
|
|
85
|
+
expect(formattedPriceForBags).toEqual({ amount: 151, currency: "GBP" });
|
|
86
|
+
|
|
87
|
+
const formattedPriceForSeats = formatters.seats!(100, "GBP", {} as any);
|
|
88
|
+
expect(formattedPriceForSeats).toEqual({ amount: 210, currency: "GBP" });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should pass through pre-defined priceFormatters unchanged", () => {
|
|
92
|
+
const priceFormatters = {
|
|
93
|
+
bags: (amount: number) => ({ amount: amount + 100 }),
|
|
94
|
+
seats: (amount: number) => ({ amount: amount + 200 }),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const formatters = createPriceFormatters(undefined, priceFormatters);
|
|
98
|
+
|
|
99
|
+
// Pass the same arguments to both the priceFormatters and the formatters
|
|
100
|
+
// to ensure they're the same functions.
|
|
101
|
+
expect(formatters.bags!(100, "GBP", {} as any)).toEqual(
|
|
102
|
+
priceFormatters.bags(100)
|
|
103
|
+
);
|
|
104
|
+
expect(formatters.seats!(100, "GBP", {} as any)).toEqual(
|
|
105
|
+
priceFormatters.seats(100)
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should allow markup and priceFormatters to be mixed if they're for different services", () => {
|
|
110
|
+
const markup = {
|
|
111
|
+
bags: {
|
|
112
|
+
rate: 0.1,
|
|
113
|
+
amount: 100,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const priceFormatters = {
|
|
118
|
+
seats: (amount: number) => ({ amount: amount + 100 }),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const formatters = createPriceFormatters(markup, priceFormatters);
|
|
122
|
+
|
|
123
|
+
expect(formatters.bags!(100, "GBP", {} as any)).toEqual({
|
|
124
|
+
amount: 210,
|
|
125
|
+
currency: "GBP",
|
|
126
|
+
});
|
|
127
|
+
expect(formatters.seats!(100, "GBP", {} as any)).toEqual(
|
|
128
|
+
priceFormatters.seats(100)
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should throw when both markup and priceFormatters are supplied", () => {
|
|
133
|
+
expect(() => {
|
|
134
|
+
createPriceFormatters(
|
|
135
|
+
{
|
|
136
|
+
bags: {
|
|
137
|
+
rate: 0.1,
|
|
138
|
+
amount: 100,
|
|
139
|
+
},
|
|
140
|
+
seats: {
|
|
141
|
+
rate: 0.2,
|
|
142
|
+
amount: 100,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
bags: () => ({ amount: 100 }),
|
|
147
|
+
seats: () => ({ amount: 100 }),
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
}).toThrowError();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { formatAvailableServices } from "@lib/formatAvailableServices";
|
|
2
|
+
import { OfferAvailableServiceBaggage } from "../../types/Offer";
|
|
3
|
+
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
5
|
+
const offer = require("../../fixtures/offers/off_1.json");
|
|
6
|
+
/* eslint-enable @typescript-eslint/no-var-requires */
|
|
7
|
+
|
|
8
|
+
describe("formatAvailableServices", () => {
|
|
9
|
+
it("should return the offer unchanged if no formatters are passed in", () => {
|
|
10
|
+
const result = formatAvailableServices(offer);
|
|
11
|
+
expect(result).toBe(offer);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should return the offer unchanged if the formatters are empty", () => {
|
|
15
|
+
const result = formatAvailableServices(offer, {});
|
|
16
|
+
expect(result).toEqual(offer);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should transform the price and currency of available services correctly", () => {
|
|
20
|
+
const result = formatAvailableServices(offer, {
|
|
21
|
+
bags: () => ({ amount: 100, currency: "Duffel points" }),
|
|
22
|
+
cancel_for_any_reason: () => ({ amount: 200, currency: "Duffel points" }),
|
|
23
|
+
});
|
|
24
|
+
expect(result.available_services[0].total_amount).toBe("100");
|
|
25
|
+
expect(result.available_services[0].total_currency).toBe("Duffel points");
|
|
26
|
+
|
|
27
|
+
expect(result.available_services[2].total_amount).toBe("200");
|
|
28
|
+
expect(result.available_services[2].total_currency).toBe("Duffel points");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should throw if more than one currency is used", () => {
|
|
32
|
+
const mockAvailableService = {
|
|
33
|
+
type: "baggage",
|
|
34
|
+
total_amount: "100",
|
|
35
|
+
total_currency: "GBP",
|
|
36
|
+
maximum_quantity: 1,
|
|
37
|
+
passenger_ids: [],
|
|
38
|
+
segment_ids: [],
|
|
39
|
+
metadata: {
|
|
40
|
+
type: "checked",
|
|
41
|
+
maximum_depth_cm: 25,
|
|
42
|
+
maximum_height_cm: 55,
|
|
43
|
+
maximum_length_cm: 35,
|
|
44
|
+
maximum_weight_kg: 23,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const availableServicesWithDifferentCurrencies: OfferAvailableServiceBaggage[] =
|
|
49
|
+
[
|
|
50
|
+
{
|
|
51
|
+
...mockAvailableService,
|
|
52
|
+
id: "1",
|
|
53
|
+
} as OfferAvailableServiceBaggage,
|
|
54
|
+
{
|
|
55
|
+
...mockAvailableService,
|
|
56
|
+
id: "2",
|
|
57
|
+
} as OfferAvailableServiceBaggage,
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
expect(() => {
|
|
61
|
+
formatAvailableServices(
|
|
62
|
+
{
|
|
63
|
+
...offer,
|
|
64
|
+
available_services: availableServicesWithDifferentCurrencies,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
bags: (amount, _currency, service) => {
|
|
68
|
+
return {
|
|
69
|
+
amount,
|
|
70
|
+
// Change the currency for each service,
|
|
71
|
+
// so that the function throws.
|
|
72
|
+
currency: service.id === "1" ? "GBP" : "USD",
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}).toThrow();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { formatSeatMaps } from "@lib/formatSeatMaps";
|
|
2
|
+
import { getFirstSeatElementMatchingCriteria } from "@lib/getFirstSeatElementMatchingCriteria";
|
|
3
|
+
import { SeatMapCabinRowSectionElementSeat } from "../../types/SeatMap";
|
|
4
|
+
|
|
5
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
6
|
+
const seatMaps = require("../../fixtures/seat-maps/off_1.json");
|
|
7
|
+
/* eslint-enable @typescript-eslint/no-var-requires */
|
|
8
|
+
|
|
9
|
+
describe("formatSeatMaps", () => {
|
|
10
|
+
it("should return the seat maps unchanged if no formatter is passed in", () => {
|
|
11
|
+
const result = formatSeatMaps(seatMaps);
|
|
12
|
+
expect(result).toBe(seatMaps);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should format seat maps price and currency correctly", () => {
|
|
16
|
+
const result = formatSeatMaps(seatMaps, () => ({
|
|
17
|
+
amount: 100,
|
|
18
|
+
currency: "Duffel points",
|
|
19
|
+
}));
|
|
20
|
+
const firstElementWithServices = getFirstSeatElementMatchingCriteria(
|
|
21
|
+
result,
|
|
22
|
+
(element) =>
|
|
23
|
+
element.type === "seat" && element.available_services.length > 0
|
|
24
|
+
) as SeatMapCabinRowSectionElementSeat;
|
|
25
|
+
|
|
26
|
+
expect(firstElementWithServices?.available_services[0].total_amount).toBe(
|
|
27
|
+
"100"
|
|
28
|
+
);
|
|
29
|
+
expect(firstElementWithServices?.available_services[0].total_currency).toBe(
|
|
30
|
+
"Duffel points"
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should throw if more than one currency is used", () => {
|
|
35
|
+
expect(() => {
|
|
36
|
+
// Keep track of how many times the function has been called.
|
|
37
|
+
// It's a basic hack to simulate different currencies being used,
|
|
38
|
+
// causing the error to be thrown.
|
|
39
|
+
let numberOfCalls = 0;
|
|
40
|
+
formatSeatMaps(seatMaps, () => {
|
|
41
|
+
numberOfCalls++;
|
|
42
|
+
return {
|
|
43
|
+
amount: 100,
|
|
44
|
+
currency: numberOfCalls % 2 === 0 ? "GBP" : "USD",
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}).toThrow();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getCurrencyForServices } from "@lib/getCurrencyForServices";
|
|
2
|
+
import { Offer } from "../../types/Offer";
|
|
3
|
+
|
|
4
|
+
describe("getCurrencyForServices", () => {
|
|
5
|
+
it("should return GBP when the first service has that currency", () => {
|
|
6
|
+
const offer = {
|
|
7
|
+
available_services: [
|
|
8
|
+
{
|
|
9
|
+
type: "baggage",
|
|
10
|
+
total_currency: "GBP",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
type: "baggage",
|
|
14
|
+
total_currency: "USD",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
expect(getCurrencyForServices(offer as Offer, "baggage")).toEqual("GBP");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should throw when there are no available services", () => {
|
|
22
|
+
const offer = {
|
|
23
|
+
available_services: [],
|
|
24
|
+
};
|
|
25
|
+
// Using 'any' here because we want to test the error message, not the type.
|
|
26
|
+
expect(() => getCurrencyForServices(offer as any, "baggage")).toThrow();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should throw when there are no available services matching the type requested", () => {
|
|
30
|
+
const offer = {
|
|
31
|
+
available_services: [
|
|
32
|
+
{
|
|
33
|
+
type: "seat",
|
|
34
|
+
total_currency: "GBP",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "seat",
|
|
38
|
+
total_currency: "GBP",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
expect(() => getCurrencyForServices(offer as Offer, "baggage")).toThrow();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { hasServiceOfSameMetadataTypeAlreadyBeenSelected } from "@lib/hasServiceOfSameMetadataTypeAlreadyBeenSelected";
|
|
2
|
+
import { CreateOrderPayloadService } from "../../types/CreateOrderPayload";
|
|
3
|
+
import { OfferAvailableBaggageService } from "../../types/Offer";
|
|
4
|
+
|
|
5
|
+
const availableService: OfferAvailableBaggageService = {
|
|
6
|
+
id: "available_service_id",
|
|
7
|
+
metadata: { type: "checked" },
|
|
8
|
+
// using as any because we don't need to test the whole type
|
|
9
|
+
} as any;
|
|
10
|
+
|
|
11
|
+
const selectedService: CreateOrderPayloadService = {
|
|
12
|
+
id: "selected_service_id",
|
|
13
|
+
serviceInformation: {
|
|
14
|
+
segmentId: "segment_id",
|
|
15
|
+
passengerId: "passenger_id",
|
|
16
|
+
type: "checked",
|
|
17
|
+
},
|
|
18
|
+
} as any;
|
|
19
|
+
|
|
20
|
+
describe("hasServiceOfSameMetadataTypeAlreadyBeenSelected", () => {
|
|
21
|
+
it("Should return false if no services selected", () => {
|
|
22
|
+
expect(
|
|
23
|
+
hasServiceOfSameMetadataTypeAlreadyBeenSelected(
|
|
24
|
+
[],
|
|
25
|
+
"segment_id",
|
|
26
|
+
"passenger_id",
|
|
27
|
+
availableService
|
|
28
|
+
)
|
|
29
|
+
).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("Should return false if service selected is the availableService we are checking", () => {
|
|
33
|
+
expect(
|
|
34
|
+
hasServiceOfSameMetadataTypeAlreadyBeenSelected(
|
|
35
|
+
[{ ...selectedService, id: availableService.id }],
|
|
36
|
+
"segment_id",
|
|
37
|
+
"passenger_id",
|
|
38
|
+
availableService
|
|
39
|
+
)
|
|
40
|
+
).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("Should return false if service selected are for different passenger", () => {
|
|
44
|
+
expect(
|
|
45
|
+
hasServiceOfSameMetadataTypeAlreadyBeenSelected(
|
|
46
|
+
[selectedService],
|
|
47
|
+
"segment_id",
|
|
48
|
+
"passenger_id_wont_match",
|
|
49
|
+
availableService
|
|
50
|
+
)
|
|
51
|
+
).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("Should return false if service selected are for different segment", () => {
|
|
55
|
+
expect(
|
|
56
|
+
hasServiceOfSameMetadataTypeAlreadyBeenSelected(
|
|
57
|
+
[selectedService],
|
|
58
|
+
"segment_id_wont_match",
|
|
59
|
+
"passenger_id",
|
|
60
|
+
availableService
|
|
61
|
+
)
|
|
62
|
+
).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("Should return false if service selected is of different type", () => {
|
|
66
|
+
expect(
|
|
67
|
+
hasServiceOfSameMetadataTypeAlreadyBeenSelected(
|
|
68
|
+
[selectedService],
|
|
69
|
+
"segment_id",
|
|
70
|
+
"passenger_id",
|
|
71
|
+
{ ...availableService, metadata: { type: "carry_on" } } as any
|
|
72
|
+
)
|
|
73
|
+
).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("Should return true if service selected is of same type for same passenger and segment", () => {
|
|
77
|
+
expect(
|
|
78
|
+
hasServiceOfSameMetadataTypeAlreadyBeenSelected(
|
|
79
|
+
[selectedService],
|
|
80
|
+
"segment_id",
|
|
81
|
+
"passenger_id",
|
|
82
|
+
availableService
|
|
83
|
+
)
|
|
84
|
+
).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { initializeLogger } from "@lib/logging";
|
|
2
|
+
|
|
3
|
+
const consoleSpies = {
|
|
4
|
+
log: jest.spyOn(console, "log"),
|
|
5
|
+
groupCollapsed: jest.spyOn(console, "groupCollapsed"),
|
|
6
|
+
groupEnd: jest.spyOn(console, "groupEnd"),
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe("initializeLogger", () => {
|
|
10
|
+
it("should return functions that do nothing when debugMode is false", () => {
|
|
11
|
+
const logger = initializeLogger(false);
|
|
12
|
+
logger.log("This should not be logged");
|
|
13
|
+
expect(consoleSpies.log).not.toHaveBeenCalled();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return functions that log a message when debugMode is true", () => {
|
|
17
|
+
const logger = initializeLogger(true);
|
|
18
|
+
logger.log("This should be logged");
|
|
19
|
+
|
|
20
|
+
// Twice because initializeLogger logs a message when it's called,
|
|
21
|
+
// and then the message we're testing.
|
|
22
|
+
expect(consoleSpies.log).toHaveBeenCalledTimes(2);
|
|
23
|
+
|
|
24
|
+
logger.logGroup("This should be logged", ["one", "two"]);
|
|
25
|
+
expect(consoleSpies.groupCollapsed).toHaveBeenCalledTimes(1);
|
|
26
|
+
expect(consoleSpies.groupEnd).toHaveBeenCalledTimes(1);
|
|
27
|
+
|
|
28
|
+
// Four times: once for the initializeLogger message, once for the
|
|
29
|
+
// earlier message, and twice for the array passed to logGroup.
|
|
30
|
+
expect(consoleSpies.log).toHaveBeenCalledTimes(4);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { moneyStringFormatter } from "@lib/moneyStringFormatter";
|
|
2
|
+
|
|
3
|
+
describe("moneyStringFormatter", () => {
|
|
4
|
+
it("should format known currencies correctly", () => {
|
|
5
|
+
expect(moneyStringFormatter("GBP")(10.23)).toEqual("£10.23");
|
|
6
|
+
});
|
|
7
|
+
it("should fall back to value + currency for unknown currencies", () => {
|
|
8
|
+
expect(moneyStringFormatter("Duffel house points")(10.23)).toEqual(
|
|
9
|
+
"10.23 Duffel house points"
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
/**
|
|
3
|
+
* In this file, we want to allow usage of @ts-ignore so
|
|
4
|
+
* that we can test functions with invalid props.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { hasCommonRequiredProps } from "@lib/validateProps";
|
|
8
|
+
import { Ancillaries } from "../../types/DuffelAncillariesProps";
|
|
9
|
+
|
|
10
|
+
describe("hasCommonRequiredProps", () => {
|
|
11
|
+
it("should pass when all required props are provided", () => {
|
|
12
|
+
const props = {
|
|
13
|
+
onPayloadReady: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
14
|
+
passengers: [],
|
|
15
|
+
services: ["bags"] as Ancillaries[],
|
|
16
|
+
offer_id: "offer_id",
|
|
17
|
+
client_key: "client_key",
|
|
18
|
+
};
|
|
19
|
+
expect(hasCommonRequiredProps(props)).toBeTruthy();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should fail when services is missing", () => {
|
|
23
|
+
const props = {
|
|
24
|
+
onPayloadReady: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
25
|
+
passengers: [],
|
|
26
|
+
offer_id: "offer_id",
|
|
27
|
+
client_key: "client_key",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
expect(hasCommonRequiredProps(props)).toBeFalsy();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should fail when onPayloadReady is missing", () => {
|
|
35
|
+
const props = {
|
|
36
|
+
passengers: [],
|
|
37
|
+
services: ["bags"] as Ancillaries[],
|
|
38
|
+
offer_id: "offer_id",
|
|
39
|
+
client_key: "client_key",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
expect(hasCommonRequiredProps(props)).toBeFalsy();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should fail when passengers is missing", () => {
|
|
47
|
+
const props = {
|
|
48
|
+
onPayloadReady: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
49
|
+
services: ["bags"] as Ancillaries[],
|
|
50
|
+
offer_id: "offer_id",
|
|
51
|
+
client_key: "client_key",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
expect(hasCommonRequiredProps(props)).toBeFalsy();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Aircraft {
|
|
2
|
+
/**
|
|
3
|
+
* The three-character IATA code for the aircraft
|
|
4
|
+
*/
|
|
5
|
+
iata_code: string;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Duffel's unique identifier for the aircraft
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The name of the aircraft
|
|
14
|
+
*/
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Airline {
|
|
2
|
+
/**
|
|
3
|
+
* The two-character IATA code for the airline. This may be null for non-IATA carriers.
|
|
4
|
+
*/
|
|
5
|
+
iata_code: string | null;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Duffel's unique identifier for the airline
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The name of the airline
|
|
14
|
+
*/
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { City } from "./City";
|
|
2
|
+
|
|
3
|
+
export interface Airport {
|
|
4
|
+
/**
|
|
5
|
+
* The three-character IATA code for the airport
|
|
6
|
+
*/
|
|
7
|
+
iata_code: string | null;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Duffel's unique identifier for the airport
|
|
11
|
+
*/
|
|
12
|
+
id: string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The name of the airport
|
|
16
|
+
*/
|
|
17
|
+
name: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The ISO 3166-1 alpha-2 code for the country where the airport is located
|
|
21
|
+
*/
|
|
22
|
+
iata_country_code: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The latitude position of the airport represented in Decimal degrees with 6 decimal points with a range between -90° and 90°
|
|
26
|
+
*/
|
|
27
|
+
latitude: number;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The longitude position of the airport represented in Decimal degrees with 6 decimal points with a range between -180° and 180°
|
|
31
|
+
*/
|
|
32
|
+
longitude: number;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The four-character ICAO code for the airport
|
|
36
|
+
*/
|
|
37
|
+
icao_code: string | null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The time zone of the airport, specified by name from the [tz database](https://en.wikipedia.org/wiki/Tz_database)
|
|
41
|
+
*/
|
|
42
|
+
time_zone: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The name of the city (or cities separated by a `/`) where the airport is located
|
|
46
|
+
*/
|
|
47
|
+
city_name: string;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The metropolitan area where the airport is located.
|
|
51
|
+
* Only present for airports which are registered with IATA as belonging to a metropolitan area.
|
|
52
|
+
*/
|
|
53
|
+
city: City;
|
|
54
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface City {
|
|
2
|
+
/**
|
|
3
|
+
* The three-character IATA code for the city
|
|
4
|
+
*/
|
|
5
|
+
iata_code: string;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Duffel's unique identifier for the city
|
|
9
|
+
*/
|
|
10
|
+
id: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The ISO 3166-1 alpha-2 code for the country where the city is located
|
|
14
|
+
*/
|
|
15
|
+
iata_country_code: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The name of the city
|
|
19
|
+
*/
|
|
20
|
+
name: string;
|
|
21
|
+
}
|