-
Notifications
You must be signed in to change notification settings - Fork 0
New Flight Book in Create Order #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,135 @@ | ||||||||||||
| import { IExecuteFunctions, NodeApiError } from 'n8n-workflow'; | ||||||||||||
| import { CrossmintApi } from '../../transport/CrossmintApi'; | ||||||||||||
| import { validateEmail, validateRequiredField } from '../../utils/validation'; | ||||||||||||
|
|
||||||||||||
| export async function bookFlight( | ||||||||||||
| context: IExecuteFunctions, | ||||||||||||
| api: CrossmintApi, | ||||||||||||
| itemIndex: number, | ||||||||||||
| ): Promise<any> { | ||||||||||||
| // Flight search parameters | ||||||||||||
| const originIATA = context.getNodeParameter('originIATA', itemIndex) as string; | ||||||||||||
| const destinationIATA = context.getNodeParameter('destinationIATA', itemIndex) as string; | ||||||||||||
| const departureDate = context.getNodeParameter('departureDate', itemIndex) as string; | ||||||||||||
| const cabinClass = context.getNodeParameter('cabinClass', itemIndex) as string; | ||||||||||||
| const passengerCount = context.getNodeParameter('passengerCount', itemIndex) as number; | ||||||||||||
| const flightIds = context.getNodeParameter('flightIds', itemIndex) as string; | ||||||||||||
|
|
||||||||||||
| // Passenger details | ||||||||||||
| const passengerTitle = context.getNodeParameter('passengerTitle', itemIndex) as string; | ||||||||||||
| const passengerFirstName = context.getNodeParameter('passengerFirstName', itemIndex) as string; | ||||||||||||
| const passengerLastName = context.getNodeParameter('passengerLastName', itemIndex) as string; | ||||||||||||
| const passengerBirthDate = context.getNodeParameter('passengerBirthDate', itemIndex) as string; | ||||||||||||
| const passengerGender = context.getNodeParameter('passengerGender', itemIndex) as string; | ||||||||||||
| const passengerEmail = context.getNodeParameter('recipientEmail', itemIndex) as string; | ||||||||||||
| const passengerPhone = context.getNodeParameter('passengerPhone', itemIndex) as string; | ||||||||||||
|
|
||||||||||||
| // Passport details | ||||||||||||
| const passportNumber = context.getNodeParameter('passportNumber', itemIndex) as string; | ||||||||||||
| const passportCountry = context.getNodeParameter('passportCountry', itemIndex) as string; | ||||||||||||
| const passportExpiry = context.getNodeParameter('passportExpiry', itemIndex) as string; | ||||||||||||
|
|
||||||||||||
| // Payment details | ||||||||||||
| const paymentMethod = context.getNodeParameter('paymentMethod', itemIndex) as string; | ||||||||||||
| const paymentCurrency = context.getNodeParameter('paymentCurrency', itemIndex) as string; | ||||||||||||
| const payerAddress = context.getNodeParameter('payerAddress', itemIndex) as string; | ||||||||||||
|
|
||||||||||||
| // Validation | ||||||||||||
| validateRequiredField(originIATA, 'Origin IATA', context, itemIndex); | ||||||||||||
| validateRequiredField(destinationIATA, 'Destination IATA', context, itemIndex); | ||||||||||||
| validateRequiredField(departureDate, 'Departure Date', context, itemIndex); | ||||||||||||
| validateRequiredField(passengerFirstName, 'Passenger First Name', context, itemIndex); | ||||||||||||
| validateRequiredField(passengerLastName, 'Passenger Last Name', context, itemIndex); | ||||||||||||
| validateEmail(passengerEmail, context, itemIndex); | ||||||||||||
| validateRequiredField(passportNumber, 'Passport Number', context, itemIndex); | ||||||||||||
|
|
||||||||||||
| try { | ||||||||||||
| // Step 1: Search for flights using Worldstore Search API | ||||||||||||
| const flightIdsArray = flightIds.split(',').map(id => id.trim()); | ||||||||||||
|
|
||||||||||||
| const searchBody = { | ||||||||||||
| uid: { | ||||||||||||
| originIATA: originIATA, | ||||||||||||
| destinationIATA: destinationIATA, | ||||||||||||
| cabinClass: cabinClass, | ||||||||||||
| passenger_number: passengerCount, | ||||||||||||
| departureFlightDetails: { | ||||||||||||
| departureDate, | ||||||||||||
| flightIds: flightIdsArray, | ||||||||||||
| }, | ||||||||||||
| }, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| const searchResponse = await api.post('ws/search', searchBody, 'unstable'); | ||||||||||||
| const { listings } = searchResponse; | ||||||||||||
|
|
||||||||||||
| if (!listings || listings.length === 0) { | ||||||||||||
| throw new Error('No flights found for the specified criteria'); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // Use the first available listing | ||||||||||||
| const selectedListing = listings[0]; | ||||||||||||
|
|
||||||||||||
| // Step 2: Create Worldstore order with passenger details | ||||||||||||
| const passengers = [{ | ||||||||||||
| title: passengerTitle, | ||||||||||||
| given_name: passengerFirstName, | ||||||||||||
| family_name: passengerLastName, | ||||||||||||
| born_on: passengerBirthDate, | ||||||||||||
| gender: passengerGender, | ||||||||||||
| email: passengerEmail, | ||||||||||||
| phone_number: passengerPhone, | ||||||||||||
| identity_documents: [{ | ||||||||||||
| type: 'passport', | ||||||||||||
| unique_identifier: passportNumber, | ||||||||||||
| issuing_country_code: passportCountry, | ||||||||||||
| expires_on: passportExpiry, | ||||||||||||
| }], | ||||||||||||
| }]; | ||||||||||||
|
|
||||||||||||
| const wsOrderBody = { | ||||||||||||
| sellerId: '1', | ||||||||||||
| items: [{ | ||||||||||||
| listingId: selectedListing.id, | ||||||||||||
| listingParameters: { | ||||||||||||
| passengers, | ||||||||||||
| }, | ||||||||||||
| }], | ||||||||||||
| orderParameters: {}, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| const wsOrderResponse = await api.post('ws/orders', wsOrderBody, 'unstable'); | ||||||||||||
| const { order } = wsOrderResponse; | ||||||||||||
|
|
||||||||||||
| // Step 3: Create Crossmint payment order | ||||||||||||
| const payment: any = { | ||||||||||||
| receiptEmail: passengerEmail, | ||||||||||||
| method: paymentMethod, | ||||||||||||
| currency: paymentCurrency, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| if (payerAddress) { | ||||||||||||
| payment.payerAddress = payerAddress; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| const crossmintOrderBody = { | ||||||||||||
| recipient: { | ||||||||||||
| email: passengerEmail, | ||||||||||||
| }, | ||||||||||||
| locale: 'en-US', | ||||||||||||
| payment, | ||||||||||||
| externalOrder: order, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| const crossmintOrderResponse = await api.post('orders', crossmintOrderBody, '2022-06-09'); | ||||||||||||
|
|
||||||||||||
| return { | ||||||||||||
| flightSearch: searchResponse, | ||||||||||||
| worldstoreOrder: wsOrderResponse, | ||||||||||||
| crossmintOrder: crossmintOrderResponse, | ||||||||||||
| selectedFlight: selectedListing, | ||||||||||||
| }; | ||||||||||||
| } catch (error: any) { | ||||||||||||
| throw new NodeApiError(context.getNode(), error); | ||||||||||||
|
Comment on lines
+132
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Missing error logging before re-throwing the error for debugging purposes
Suggested change
Context Used: Rule from |
||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+46
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Function is 89 lines long and should be broken down into smaller helper functions for better maintainability Context Used: Rule from |
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,13 +4,20 @@ import { API_VERSIONS } from '../../utils/constants'; | |
| import { validateEmail, validateRequiredField, validateAddressFields } from '../../utils/validation'; | ||
| import { buildProductLocator } from '../../utils/locators'; | ||
| import { OrderCreateRequest } from '../../transport/types'; | ||
| import { bookFlight } from './bookFlight.operation'; | ||
|
|
||
| export async function findProduct( | ||
| context: IExecuteFunctions, | ||
| api: CrossmintApi, | ||
| itemIndex: number, | ||
| ): Promise<any> { | ||
| const platform = context.getNodeParameter('platform', itemIndex) as string; | ||
|
|
||
| // If platform is flight, delegate to bookFlight operation | ||
| if (platform === 'flight') { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: String literal comparison instead of using enum constant. Consider defining platform constants for consistency and type safety. Context Used: Rule from |
||
| return await bookFlight(context, api, itemIndex); | ||
| } | ||
|
Comment on lines
+16
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: The findProduct function now handles both product finding and flight booking, violating single responsibility principle. Consider renaming this function to something more generic like 'processOrder' or creating separate operations entirely. |
||
|
|
||
| const productIdentifier = context.getNodeParameter('productIdentifier', itemIndex) as string; | ||
| const recipientEmail = context.getNodeParameter('recipientEmail', itemIndex) as string; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Missing validation for passport-related fields (passportCountry, passportExpiry) which are required for flight bookings