Fixed idempotency bug and added explicit idempotency test.
diff --git a/api/api.go b/api/api.go index 4379df2..be23323 100644 --- a/api/api.go +++ b/api/api.go
@@ -21,7 +21,9 @@ "errors" "fmt" "log" + "math/rand" "sort" + "strconv" "time" "github.com/golang/protobuf/proto" @@ -117,6 +119,8 @@ if err != nil { return nil, fmt.Errorf("unable to build request for check availability flow. err: %v, availability record: %v", err, a.String()) } + + gen := rand.New(rand.NewSource(time.Now().UnixNano())) // Lease currently unsupported. req := &mpb.CreateBookingRequest{ Slot: slot, @@ -130,7 +134,7 @@ PaymentInformation: &mpb.PaymentInformation{ PrepaymentStatus: mpb.PrepaymentStatus_PREPAYMENT_NOT_PROVIDED, }, - IdempotencyToken: utils.BuildIdempotencyToken(a, userID), + IdempotencyToken: strconv.Itoa(gen.Intn(1000000)), } log.Printf("CreateBooking Request. Sent(unix): %s, Request %s", time.Now().UTC().Format(time.RFC850), req.String()) @@ -152,6 +156,19 @@ }); iE != nil { return nil, fmt.Errorf("invalid response. CreateBooking invalid: %s", iE.Error()) } + + // Perform idempotency test. + log.Printf("Idempotency check -- CreateBooking Request. Sent(unix): %s, Request %s", time.Now().UTC().Format(time.RFC850), req.String()) + idemResp, err := c.CreateBooking(ctx, req) + if err != nil { + return nil, fmt.Errorf("invalid response. Idempotency check yielded error: %v", err) + } + + log.Printf("Idempotency check -- Response. Received(unix): %s, Response %s", time.Now().UTC().Format(time.RFC850), resp.String()) + if diff := cmp.Diff(idemResp, resp); diff != "" { + return b, fmt.Errorf("Idempotency check invalid (-got +want)\n%s", diff) + } + return b, nil }
diff --git a/utils/utils.go b/utils/utils.go index c082630..db7d129 100644 --- a/utils/utils.go +++ b/utils/utils.go
@@ -18,14 +18,12 @@ package utils import ( - "crypto/md5" "errors" "fmt" "io/ioutil" "log" "math/rand" "path" - "strconv" "strings" "time" @@ -108,24 +106,16 @@ return fmt.Errorf("payment information differs (-got +want)\n%s", diff) } // BookingStatus_CONFIRMED is the default case unless want overrides it. - wantStatus := mpb.BookingStatus_CONFIRMED - if want.GetStatus() != mpb.BookingStatus_BOOKING_STATUS_UNSPECIFIED { - wantStatus = want.GetStatus() - } + wantStatus := mpb.BookingStatus_CONFIRMED + if want.GetStatus() != mpb.BookingStatus_BOOKING_STATUS_UNSPECIFIED { + wantStatus = want.GetStatus() + } if diff := cmp.Diff(got.GetStatus(), wantStatus); diff != "" { return fmt.Errorf("status differs (-got +want)\n%s", diff) } return nil } -// BuildIdempotencyToken creates a unique token based on the slot and user attempting to book the slot. -func BuildIdempotencyToken(a *fpb.Availability, u string) string { - r := a.GetResources() - parts := []string{a.GetMerchantId(), a.GetServiceId(), strconv.FormatInt(a.GetStartSec(), 10), strconv.FormatInt(a.GetDurationSec(), 10), r.GetStaffId(), r.GetRoomId(), u} - key := strings.Join(parts, "") - return fmt.Sprintf("%x", md5.Sum([]byte(key))) -} - // BuildSlotFrom creates a bookingservice slot from an feed availability record. func BuildSlotFrom(a *fpb.Availability) (*mpb.Slot, error) { startTime, err := ptypes.TimestampProto(time.Unix(a.GetStartSec(), 0))