This repository is meant to help simplify PowerShell scripting against Amadeus's SOAP API in a relatively simple way with clean code. As part of the effort, missing functionality in PowerShell is also implemented, mainly to handle the lifetime of SOAP proxies and process the complicated request and response objects using [JSONPath] syntax. Although the original goal is Amadeus, the solution works with all kinds of SOAP APIs and overall improves the PowerShell experience with SOAP.
Note that the author is not an expert in Amadeus or its API and this repository is only meant to provide a simpler experience with the API based on the following considerations.
Amadeus's GDS also known as 1A has the following attributes that are considered as part of this solution:
- 1A creates offers 1 endpoint that is a composition of operations from different functional areas. For example an interface that has the following operations:
- PNR_Retrieve
- PNR_Cancel
- Security_SignOut
- With the endpoint being a dynamically different for each "customer", the intention is not wrapper cmdlets per operation but focuse on a simple and clean interaction with 1A's SOAP API.
- Each operation has a very composite request structure and an even more complicated one for response.
- 1A SOAP API is stateful similar to the ARD console. There are two options:
- A request contains all the necessary information for a specific intention.
- Use a session header to control start, continue or end the session with each API invocation.
- When importing a SOAP proxy in PowerShell, then behind the scenes, the .NET's Windows Communication Foundation generates in memory assemblies and types based on the imported WSDL. PowerShell keeps such types in the memory for the entire duration of the session and that can complicate things a lot when recreating the SOAP proxy.
- When working with SOAP proxies, dynamic objects cannot be used, because everything needs to use the types generated from the WSDL, to allow generation of the SOAP envelope through the DataContract annotations.
- Instead of generating SOAP envelops and POSTing them to the 1A endpoint, use the New-WebServiceProxy cmdlet to generate a typed proxy and work with typed memory objects.
- *Note that New-WebServiceProxy is not implemented in PowerShell Core (v6), since the underlying .NET Core doesn't support the dependent assemblies. For this reason, this module SOAPProxy requires PowerShell 5.1. To help with this limitation, an issue has been submitted with the powershell team.
- With composite and nested type objects, the code becomes very verbose in order to set a property or find objects based on a condition.
For example for the PNR_Retrieve
request we want to use expressions like retrievalFacts.retrieve.type
to set or refer to the matching object in the respected XML:
<retrievalFacts>
<retrieve>
<type>2</type>
</retrieve>
<reservationOrProfileIdentifier>
<reservation>
<companyId>SN</companyId>
<controlNumber>MTHOH2</controlNumber>
</reservation>
</reservationOrProfileIdentifier>
</retrievalFacts>
The repository is composed by 3 modules. Each can be isolated and has the potential to become an independent repository. For the beginning, all will be placed within this repository with isolation in mind.
- JSONPath that provides basic functionality similar to Stefan's Goessner JSONPath concept. Note that although this is not a full implementation of his idea, it extends it to drive
Set
,Get
,Test
andFind
operations. - SOAPProxy that provides some basic functionality around New-WebServiceProxy to address the dynamic injection of .NET assemblies into the session.
- 1ASOAP that provides functionality around the 1A
Session
andAMA_SecurityHostedUser
headers
A simple PNR_Retrieve
# Initialize Proxy
$proxy=Initialize-1ASOAPProxy -Uri $URI -Namespace "amadeus.soap.example" -AsDefault -PassThru
try
{
# Create the PNR_Retrieve request object
$pnrRetrieve = $proxy |New-1ASOAPRequest -Operation PNR_Retrieve
# Fill in the structure of the PNR_Retrieve request
$pnrRetrieve |
Set-JSONPath -Path "retrievalFacts.retrieve.type" -Value 2 -PassThru |
Set-JSONPath -Path "retrievalFacts.reservationOrProfileIdentifier.companyId" -Value "SN" -PassThru |
Set-JSONPath -Path "retrievalFacts.reservationOrProfileIdentifier.controlNumber" -Value $pnr
# Invoke PNR_Retrieve operation over 1A
$pnrRetrieveResponse = $proxy|Invoke-1AOperation -Operation PNR_Retrieve -Parameter $pnrRetrieve
# Capture all dataelements having ssr type CTCE
$dataElementsIndivWithCTCE=$pnrRetrieveResponse.dataElementsMaster.dataElementsIndiv |
Find-JSONPath -Path"serviceRequest.ssr.type" -EQ -Value "CTCE"
}
finally
{
# Stop the 1A session present in the proxy
Stop-1ASOAPSession
}
Calculate SOF (Seat Occupancy Factor) and export to CSV
# Initialize Proxy
$proxy=Initialize-1ASOAPProxy -Uri $URI -Namespace "amadeus.soap.example" -AsDefault -PassThru
try
{
# Create the request object
$invAdvancedGetFlightDataRequest = $proxy |New-SOAPProxyRequest -Operation Inv_AdvancedGetFlightData
# Fill in the structure of the Inv_AdvancedGetFlightData request
$invAdvancedGetFlightDataRequest |
Set-JSONPath -Path "flightDate.airlineInformation.airlineCode" -Value "SN" -PassThru |
Set-JSONPath -Path "flightDate.flightReference.flightNumber" -Value $flight.Number -PassThru |
Set-JSONPath -Path "flightDate.departureDate" -Value $flight.Date -PassThru |
Set-JSONPath -Path "flightDate.boardPoint" -Value $flight.Origin -PassThru |
Set-JSONPath -Path "flightDate.offPoint" -Value $flight.Destination -PassThru |
Set-JSONPath -Path "grabLock[0].statusIndicator" -Value "GBL" -PassThru |
Set-JSONPath -Path "grabLock[0].actionRequest" -Value "2"
# Invoke PNR_Retrieve operation over 1A
$invAdvancedGetFlightDataResponse = $proxy|Invoke-1ASOAPOperation -Operation Inv_AdvancedGetFlightData -Parameter $invAdvancedGetFlightDataRequest
# Process data to create a report per leg with SOF, CabinCode, BookingCounter and Capacity
$csvData=$invAdvancedGetFlightDataResponse.flightDateInformation.legs |ForEach-Object {
#Iterate on eash leg
$leg=$_
$leg.legCabins |ForEach-Object {
# Iterate on each cabin within each leg
$legCabin=$_
# Extract the configuration with qualifier O
$configuration=$_.legCabin|Where-Object -Property qualifier -EQ "O"
$bookingCounter=$_.cabinAvailabilities.bookingsCounter
# calculate the SOF
$sof=$bookingCounter/($configuration.cabinCapacity)
# Create a new instance to put in the csv
New-Object -TypeName psobject -Property @{
Origin=$leg.leg.legDetails[0]
Destination=$leg.leg.legDetails[1]
CabinCode=$configuration.cabinCode
Capacity=$configuration.cabinCapacity
BookingCounter=$bookingCounter
SOF=$sof
}
}
}
$csvData|ConvertTo-Csv|Out-File -FilePath $csvPath -Force
}
finally
{
# Stop the 1A session present in the proxy
Stop-1ASOAPSession
}