Skip to content

Commit

Permalink
Merge pull request #2 from ailgroup/hotelws_phase2
Browse files Browse the repository at this point in the history
Hotelws phase2
  • Loading branch information
jbowles authored May 22, 2018
2 parents 1e31b2d + 5d5bba8 commit 90c6431
Show file tree
Hide file tree
Showing 20 changed files with 1,882 additions and 1,073 deletions.
6 changes: 3 additions & 3 deletions hotelws/hotel_avail_rq.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (a *OTAHotelAvailRQ) addCustomerID(cID string) {
}

// SetHotelAvailRqStruct hotel availability request using input parameters
func SetHotelAvailRqStruct(guestCount int, query HotelSearchCriteria, arrive, depart string) HotelAvailBody {
func SetHotelAvailRqStruct(guestCount int, query *HotelSearchCriteria, arrive, depart string) HotelAvailBody {
a, d := arriveDepartParser(arrive, depart)
return HotelAvailBody{
OTAHotelAvailRQ: OTAHotelAvailRQ{
Expand All @@ -72,9 +72,9 @@ func SetHotelAvailRqStruct(guestCount int, query HotelSearchCriteria, arrive, de
XMLNSXsi: srvc.BaseXSINamespace,
ReturnHostCommand: returnHostCommand,
Avail: AvailRequestSegment{
GuestCounts: GuestCounts{Count: guestCount},
GuestCounts: &GuestCounts{Count: guestCount},
HotelSearchCriteria: query,
ArriveDepart: TimeSpan{
TimeSpan: &TimeSpan{
Depart: d.Format(timeSpanFormatter),
Arrive: a.Format(timeSpanFormatter),
},
Expand Down
9 changes: 5 additions & 4 deletions hotelws/hotel_avail_rq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func TestMultipleHotelCriteria(t *testing.T) {
}

func TestSetHotelAvailRqStructMarshal(t *testing.T) {
availBody := SetHotelAvailRqStruct(sampleGuestCount, HotelSearchCriteria{}, sampleArrive, sampleDepart)
availBody := SetHotelAvailRqStruct(sampleGuestCount, &HotelSearchCriteria{}, sampleArrive, sampleDepart)
avail := availBody.OTAHotelAvailRQ
avail.addCorporateID(sampleCID)

Expand All @@ -198,7 +198,7 @@ func TestSetHotelAvailRqStructMarshal(t *testing.T) {
}

func TestSetHotelAvailRqStructCorpID(t *testing.T) {
availBody := SetHotelAvailRqStruct(sampleGuestCount, HotelSearchCriteria{}, sampleArrive, sampleDepart)
availBody := SetHotelAvailRqStruct(sampleGuestCount, &HotelSearchCriteria{}, sampleArrive, sampleDepart)
avail := availBody.OTAHotelAvailRQ
avail.addCorporateID(sampleCID)

Expand All @@ -222,6 +222,7 @@ func TestAvailIdsMarshal(t *testing.T) {

avail := availBody.OTAHotelAvailRQ
avail.addCorporateID(sampleCID)
avail.Avail.RatePlanCandidates = SetRateParams([]RatePlan{RatePlan{CurrencyCode: "USD", DCA_ProductCode: "I7A"}})

if avail.Avail.GuestCounts.Count != gcount {
t.Errorf("SetHotelAvailRqStruct GuestCounts.Count expect: %d, got %d", gcount, avail.Avail.GuestCounts.Count)
Expand All @@ -235,8 +236,8 @@ func TestAvailIdsMarshal(t *testing.T) {
if err != nil {
t.Error("Error marshaling get hotel content", err)
}
if string(b) != string(sampleAvailRQHotelIDSCoprID) {
t.Errorf("Expected marshal hotel avail for hotel ids \n sample: %s \n result: %s", string(sampleAvailRQHotelIDSCoprID), string(b))
if string(b) != string(sampleAvailRQHotelIDSCoprIDRatePlans) {
t.Errorf("Expected marshal hotel avail for hotel ids \n sample: %s \n result: %s", string(sampleAvailRQHotelIDSCoprIDRatePlans), string(b))
}
//fmt.Printf("content marshal \n%s\n", b)
}
Expand Down
18 changes: 4 additions & 14 deletions hotelws/hotel_prop_desc_rq.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (a *HotelPropDescRQ) addCustomerID(cID string) {
}

// SetHotelPropDescRqStruct hotel availability request using input parameters
func SetHotelPropDescRqStruct(guestCount int, query HotelSearchCriteria, arrive, depart string) (HotelPropDescBody, error) {
func SetHotelPropDescRqStruct(guestCount int, query *HotelSearchCriteria, arrive, depart string) (HotelPropDescBody, error) {
err := query.validatePropertyRequest()
if err != nil {
return HotelPropDescBody{}, err
Expand All @@ -66,9 +66,9 @@ func SetHotelPropDescRqStruct(guestCount int, query HotelSearchCriteria, arrive,
XMLNSXsi: srvc.BaseXSINamespace,
ReturnHostCommand: true,
Avail: AvailRequestSegment{
GuestCounts: GuestCounts{Count: guestCount},
GuestCounts: &GuestCounts{Count: guestCount},
HotelSearchCriteria: query,
ArriveDepart: TimeSpan{
TimeSpan: &TimeSpan{
Depart: d.Format(timeSpanFormatter),
Arrive: a.Format(timeSpanFormatter),
},
Expand Down Expand Up @@ -110,17 +110,6 @@ func BuildHotelPropDescRequest(from, pcc, binsectoken, convid, mid, time string,
}
}

type RoomStay struct {
XMLName xml.Name `xml:"RoomStay"`
BasicPropertyInfo BasicPropertyInfo
RoomRates []RoomRate `xml:"RoomRates>RoomRate"`
TimeSpan struct {
Duration int `xml:"Duration,attr"` //string 0001 or int 1?
End string `xml:"End,attr"`
Start string `xml:"Start,attr"`
} `xml:"TimeSpan"`
}

// OTAHotelAvailRS parse sabre hotel availability
type HotelPropertyDescriptionRS struct {
XMLName xml.Name `xml:"HotelPropertyDescriptionRS"`
Expand Down Expand Up @@ -149,6 +138,7 @@ type HotelPropDescResponse struct {
func CallHotelPropDesc(serviceURL string, req HotelPropDescRequest) (HotelPropDescResponse, error) {
propResp := HotelPropDescResponse{}
byteReq, _ := xml.Marshal(req)
//fmt.Printf("REQ: \n\n %s \n\n", byteReq)

//post payload
resp, err := http.Post(serviceURL, "text/xml", bytes.NewBuffer(byteReq))
Expand Down
4 changes: 2 additions & 2 deletions hotelws/hotel_prop_desc_rq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestPropDescBuildHotelPropDescMarshal(t *testing.T) {
}

func TestSetHotelPropDescRqStructCorpID(t *testing.T) {
body, _ := SetHotelPropDescRqStruct(sampleGuestCount, HotelSearchCriteria{}, sampleArrive, sampleDepart)
body, _ := SetHotelPropDescRqStruct(sampleGuestCount, &HotelSearchCriteria{}, sampleArrive, sampleDepart)
prop := body.HotelPropDescRQ
prop.addCorporateID(sampleCID)

Expand All @@ -81,7 +81,7 @@ func TestPropDescUnmarshal(t *testing.T) {
prop := HotelPropDescResponse{}
err := xml.Unmarshal(sampleHotelPropDescRSgood, &prop)
if err != nil {
t.Errorf("Error unmarshaling hotel avail %s \nERROR: %v", sampleHotelAvailRSgood, err)
t.Fatalf("Error unmarshaling hotel avail %s \nERROR: %v", sampleHotelAvailRSgood, err)
}
reqError := prop.Body.HotelDesc.Result.Error
if reqError.Type != "" {
Expand Down
50 changes: 11 additions & 39 deletions hotelws/hotel_rate_desc_rq.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/ailgroup/sbrweb/srvc"
)

// HotelRateDescRequest for soap package on HotelRateertyDescriptionRQ service
// HotelRateDescRequest for soap package on HotelRateDescRequest service
type HotelRateDescRequest struct {
srvc.Envelope
Header srvc.SessionHeader
Expand All @@ -33,31 +33,8 @@ type HotelRateDescRQ struct {
Avail AvailRequestSegment
}

// addCorporateID to the existing avail struct for a corporate customer
func (a *HotelRateDescRQ) addCorporateID(cID string) {
a.Avail.Customer = &Customer{
Corporate: &Corporate{
ID: cID,
},
}
}

// addCustomerID rateID to the existing avail struct for a corporate customer
func (a *HotelRateDescRQ) addCustomerID(cID string) {
a.Avail.Customer = &Customer{
CustomerID: &CustomerID{
Number: cID,
},
}
}

// SetHotelRateDescRqStruct hotel availability request using input parameters
func SetHotelRateDescRqStruct(guestCount int, query HotelSearchCriteria, arrive, depart string) (HotelRateDescBody, error) {
err := query.validatePropertyRequest()
if err != nil {
return HotelRateDescBody{}, err
}
a, d := arriveDepartParser(arrive, depart)
// SetHotelRateDescRqStruct hotel rate description request using input parameters
func SetHotelRateDescRqStruct(rpc *RatePlanCandidates) (HotelRateDescBody, error) {
return HotelRateDescBody{
HotelRateDescRQ: HotelRateDescRQ{
Version: hotelRQVersion,
Expand All @@ -66,18 +43,13 @@ func SetHotelRateDescRqStruct(guestCount int, query HotelSearchCriteria, arrive,
XMLNSXsi: srvc.BaseXSINamespace,
ReturnHostCommand: true,
Avail: AvailRequestSegment{
GuestCounts: GuestCounts{Count: guestCount},
HotelSearchCriteria: query,
ArriveDepart: TimeSpan{
Depart: d.Format(timeSpanFormatter),
Arrive: a.Format(timeSpanFormatter),
},
RatePlanCandidates: rpc,
},
},
}, nil
}

// BuildHotelRateDescRequest to make hotel property description request, which will have rate availability information on the response.
// BuildHotelRateDescRequest to make hotel property description request, done after hotel property description iff HRD_RequiredForSell=true.
func BuildHotelRateDescRequest(from, pcc, binsectoken, convid, mid, time string, propDesc HotelRateDescBody) HotelRateDescRequest {
return HotelRateDescRequest{
Envelope: srvc.CreateEnvelope(),
Expand All @@ -93,8 +65,8 @@ func BuildHotelRateDescRequest(from, pcc, binsectoken, convid, mid, time string,
},
CPAID: pcc,
ConversationID: convid,
Service: srvc.ServiceElem{Value: "HotelRateertyDescription", Type: "sabreXML"},
Action: "HotelRateertyDescriptionLLSRQ",
Service: srvc.ServiceElem{Value: "HotelRateDescriptionLLSRQ", Type: "sabreXML"},
Action: "HotelRateDescriptionLLSRQ",
MessageData: srvc.MessageDataElem{
MessageID: mid,
Timestamp: time,
Expand All @@ -110,7 +82,7 @@ func BuildHotelRateDescRequest(from, pcc, binsectoken, convid, mid, time string,
}
}

// HotelRateDescriptionRS parse sabre hotel availability
// HotelRateDescriptionRS parse sabre hotel rate description
type HotelRateDescriptionRS struct {
XMLName xml.Name `xml:"HotelRateDescriptionRS"`
XMLNS string `xml:"xmlns,attr"`
Expand All @@ -122,7 +94,7 @@ type HotelRateDescriptionRS struct {
RoomStay RoomStay
}

// HotelAvailResponse is wrapper with namespace prefix definitions for payload
// HotelRateDescResponse is wrapper with namespace prefix definitions for payload
type HotelRateDescResponse struct {
Envelope srvc.EnvelopeUnMarsh
Header srvc.SessionHeaderUnmarsh
Expand All @@ -134,8 +106,8 @@ type HotelRateDescResponse struct {
ErrorSabreXML ErrorSabreXML
}

// CallHotelRate to sabre web services retrieve hotel rates using HotelRateertyDescriptionLLSRQ.
func CallHotelRate(serviceURL string, req HotelRateDescRequest) (HotelRateDescResponse, error) {
// CallHotelRateDesc to sabre web services retrieve hotel rates using HotelRateDescriptionLLSRQ. This call only supports requests that contain an RPH from a previous hotel_property_desc call, see BuildHotelRateDescRequest.
func CallHotelRateDesc(serviceURL string, req HotelRateDescRequest) (HotelRateDescResponse, error) {
propResp := HotelRateDescResponse{}
byteReq, _ := xml.Marshal(req)

Expand Down
141 changes: 141 additions & 0 deletions hotelws/hotel_rate_desc_rq_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package hotelws

import (
"encoding/xml"
"testing"
)

func TestHotelRateDescMarshal(t *testing.T) {
rpc := SetRateParams(
[]RatePlan{
RatePlan{
RPH: 12,
},
},
)
rate, err := SetHotelRateDescRqStruct(rpc)
if err != nil {
t.Error("Error SetHotelRateDescRqStruct:", err)
}
req := BuildHotelRateDescRequest(samplesite, samplepcc, samplebinsectoken, sampleconvid, samplemid, sampletime, rate)

b, err := xml.Marshal(req)
if err != nil {
t.Error("Error marshaling get hotel content", err)
}

if string(b) != string(sampleHotelRateDescRQRPH) {
t.Errorf("Expected marshal SOAP hotel rate description for rph \n sample: %s \n result: %s", string(sampleHotelRateDescRQRPH), string(b))
}
//fmt.Printf("content marshal \n%s\n", b)
}

var additionalCards = []string{"DS", "CA", "MC", "CB", "VI", "VS", "AX", "JC", "DC"}
var guaranteeCards = []struct {
code string
name string
}{
{"AX", "AMERICAN EXPRESS"},
{"CA", "MASTERCARD"},
{"CB", "CARTE BLANCHE"},
{"DC", "DINERS CLUB CARD"},
{"DS", "DISCOVER CARD"},
{"JC", "JCB CREDIT CARD"},
{"VI", "VISA"},
}

func TestRateDescCall(t *testing.T) {
// assume RPH is from previous hotel property description call
rpc := SetRateParams(
[]RatePlan{
RatePlan{
RPH: 12,
},
},
)
raterq, _ := SetHotelRateDescRqStruct(rpc)

req := BuildHotelRateDescRequest(samplesite, samplepcc, samplebinsectoken, sampleconvid, samplemid, sampletime, raterq)

resp, err := CallHotelRateDesc(serverHotelRateDesc.URL, req)
if err != nil {
t.Error("Error making request CallHotelRateDesc", err)
}
if resp.Body.Fault.String != "" {
t.Errorf("Body.Fault.String expect empty: '%s', got: %s", "", resp.Body.Fault.String)
}

for i, cg := range resp.Body.HotelDesc.RoomStay.Guarantee.DepositsAccepted.PaymentCards {
if cg.Code != guaranteeCards[i].code {
t.Errorf("Guarantee.DepositsAccepted.PaymentCards[%d].Code expect: %s, got: %s", i, guaranteeCards[i].code, cg.Code)
}
if cg.Type != guaranteeCards[i].name {
t.Errorf("Guarantee.DepositsAccepted.PaymentCards[%d].Type expect: %s, got: %s", i, guaranteeCards[i].name, cg.Type)
}
}

roomStayRates := resp.Body.HotelDesc.RoomStay.RoomRates
numRoomRates := len(roomStayRates)
if numRoomRates != 1 {
t.Error("Number of room rates is wrong")
}

rr := roomStayRates[0]
if rr.IATA_Character != "J1KA16" {
t.Errorf("IATA_Character expected %s, got %s", "J1KA16", rr.IATA_Character)
}
if rr.GuaranteeSurcharge != "G" {
t.Errorf("GuaranteeSurcharge expected %s, got %s", "G", rr.GuaranteeSurcharge)
}
if len(rr.AdditionalInfo.PaymentCards) != 9 {
t.Errorf("AdditionalInfo.PaymentCards count is wrong: %v", rr.AdditionalInfo)
}
for idx, card := range rr.AdditionalInfo.PaymentCards {
if card.Code != additionalCards[idx] {
t.Errorf("AdditionalInfo.PaymentCards expect: %s, got: %s", additionalCards[idx], card.Code)
}
}

cnum := rr.AdditionalInfo.CancelPolicy.Numeric
copt := rr.AdditionalInfo.CancelPolicy.Option
if cnum != 2 {
t.Errorf("RoomRate expected cancel policy numeric %d, got %d", 2, cnum)
}
if copt != "D" {
t.Errorf("RoomRate expected cancel policy option %s, got %s", "D", copt)
}

numRates := len(roomStayRates[0].Rates)
if numRates != 1 {
t.Error("Number of rates is wrong")
}
rate := rr.Rates[0]
if rate.Amount != "274.55" {
t.Errorf("Rate expected %s, got %s", "274.55", rate.Amount)
}
if rate.CurrencyCode != "USD" {
t.Errorf("CurrencyCode expected %s, got %s", "USD", rate.CurrencyCode)
}
if rate.HRD_RequiredForSell != "false" {
t.Errorf("CurrencyCode expected %s, got %s", "false", rate.HRD_RequiredForSell)
}

hprice := rate.HotelPricing
if hprice.Amount != "307.50" {
t.Errorf("HotelPricing expected empty %s, got %s", "307.50", hprice.Amount)
}
if hprice.TotalSurcharges.Amount != "" {
t.Errorf("TotalSurcharges expected %s, got %s", "", hprice.TotalSurcharges.Amount)
}
if hprice.TotalTaxes.Amount != "32.95" {
t.Errorf("TotalTaxes expected %s, got %s", "32.95", hprice.TotalTaxes.Amount)
}

if hprice.TotalTaxes.TaxFieldOne != "19.22" {
t.Errorf("TaxeFieldOnw expected %s, got %s", "19.22", hprice.TotalTaxes.TaxFieldOne)
}
if hprice.TotalTaxes.TaxFieldTwo != "13.73" {
t.Errorf("TaxFieldTwo expected %s, got %s", "13.73", hprice.TotalTaxes.TaxFieldTwo)
}

}
16 changes: 13 additions & 3 deletions hotelws/hotel_search_criteria.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,26 @@ import (
"strings"
)

// SetRateParams helper to create a slice of rate plans to append on a an Avail Segement
// for search or description services
func SetRateParams(ratePlans []RatePlan) *RatePlanCandidates {
rpc := &RatePlanCandidates{}
for _, plan := range ratePlans {
rpc.RatePlans = append(rpc.RatePlans, &plan)
}
return rpc
}

// NewHotelSearchCriteria accepts set of QueryParams functions, executes over hotel search criteria and returns modified criteria
func NewHotelSearchCriteria(queryParams ...QueryParams) (HotelSearchCriteria, error) {
func NewHotelSearchCriteria(queryParams ...QuerySearchParams) (*HotelSearchCriteria, error) {
criteria := &HotelSearchCriteria{}
for _, qm := range queryParams {
err := qm(criteria)
if err != nil {
return *criteria, err
return criteria, err
}
}
return *criteria, nil
return criteria, nil
}

// validatePropertyRequest ensures property description requests are well-formed
Expand Down
Loading

0 comments on commit 90c6431

Please sign in to comment.