From d8a8309c44e002d794fd563ec174ac95bca9e036 Mon Sep 17 00:00:00 2001 From: m-timmermann Date: Fri, 15 Mar 2024 08:37:13 +0100 Subject: [PATCH 1/6] add-availabilities.issue-4041 * add Availabilities --- availabilities/availabilities.go | 365 +++++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 availabilities/availabilities.go diff --git a/availabilities/availabilities.go b/availabilities/availabilities.go new file mode 100644 index 0000000..0c8601e --- /dev/null +++ b/availabilities/availabilities.go @@ -0,0 +1,365 @@ +// Copyright (c) 2013-present Meplato GmbH. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under +// the License. + +// Package availabilities implements the Meplato Store API. +// +// See https://developer.meplato.com/store2/. +package availabilities + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/meplato/store2-go-client/v2/internal/meplatoapi" +) + +// Always reference these packages, just in case. +var ( + _ = bytes.NewBuffer + _ = http.Get + _ = fmt.Print + _ = bytes.NewBuffer + _ = json.NewDecoder + _ = errors.New + _ = fmt.Print + _ = io.Copy + _ = http.Get + _ = url.Parse + _ = strconv.Itoa + _ = strings.HasPrefix + _ = time.Parse + _ = meplatoapi.CheckResponse +) + +const ( + title = "Meplato Store API" + version = "2.2.0" + baseURL = "https://store.meplato.com/api/v2" +) + +type Service struct { + client *http.Client + BaseURL string + User string + Password string +} + +func New(client *http.Client) (*Service, error) { + if client == nil { + return nil, errors.New("client is nil") + } + return &Service{client: client, BaseURL: baseURL}, nil +} + +func (s *Service) Delete() *DeleteService { + return NewDeleteService(s) +} + +func (s *Service) Get() *GetService { + return NewGetService(s) +} + +func (s *Service) Upsert() *UpsertService { + return NewUpsertService(s) +} + +// Availability information of a product in a location +type Availability struct { + // Message: Contains the stock state description; i.e. in stock; out of + // stock; limited availability; on display to order + Message string `json:"message,omitempty"` + // Mpcc: Unique internal identifier of the merchant + Mpcc string `json:"mpcc,omitempty"` + // Quantity: Reflects the amount of items available + Quantity *float64 `json:"quantity,omitempty"` + // Region: 2-letter ISO code of the country/region where the product is + // stored + Region string `json:"region,omitempty"` + // Spn: Merchant's unique identifier of a product + Spn string `json:"spn,omitempty"` + // Updated: Update date given by the merchant i.e. Q4/2022, 2022/10/12 + Updated string `json:"updated,omitempty"` + // ZipCode: Zip code where the product is stored + ZipCode string `json:"zipCode,omitempty"` +} + +// DeleteResponse is the outcome of a successful request to delete an +// availability. +type DeleteResponse struct { + // Kind describes this entity, it will be + // store#availability/deleteResponse. + Kind string `json:"kind,omitempty"` +} + +// GetResponse is the collection of availability information for an SPN. +type GetResponse struct { + // Items: Collection of availability information associated with an SPN + // for a merchant. + Items []*Availability `json:"items,omitempty"` + // Kind is store#availability/getResponse for this kind of response. + Kind string `json:"kind,omitempty"` +} + +// UpsertRequest holds the properties of the availability information to +// create or update. +type UpsertRequest struct { + // Message: Contains the stock state description; i.e. in stock; out of + // stock; limited availability; on display to order + Message string `json:"message,omitempty"` + // Mpcc: Unique internal identifier of the merchant (optional) + Mpcc string `json:"mpcc,omitempty"` + // Quantity: Reflects the amount of items available + Quantity *float64 `json:"quantity,omitempty"` + // Region: 2-letter ISO code of the country/region where the product is + // stored + Region string `json:"region,omitempty"` + // Updated: Update date given by the merchant i.e. Q4/2022, 2022/10/12 + Updated string `json:"updated,omitempty"` + // ZipCode: Zip code where the product is stored + ZipCode string `json:"zipCode,omitempty"` +} + +// UpsertResponse is the outcome of a successful request to upsert an +// availability. +type UpsertResponse struct { + // Kind describes this entity, it will be + // store#availability/upsertResponse. + Kind string `json:"kind,omitempty"` + // Link includes the URL where this resource will be available + Link string `json:"link,omitempty"` +} + +// Delete availability information of a product. It is an asynchronous +// operation. +type DeleteService struct { + s *Service + opt_ map[string]interface{} + hdr_ map[string]interface{} + spn string +} + +// NewDeleteService creates a new instance of DeleteService. +func NewDeleteService(s *Service) *DeleteService { + rs := &DeleteService{s: s, opt_: make(map[string]interface{}), hdr_: make(map[string]interface{})} + return rs +} + +// 2-letter ISO code of the country/region where the product is stored +func (s *DeleteService) Region(region string) *DeleteService { + s.opt_["region"] = region + return s +} + +// SPN is the unique identifier of a product within a merchant. +func (s *DeleteService) Spn(spn string) *DeleteService { + s.spn = spn + return s +} + +// Zip code where the product is stored +func (s *DeleteService) ZipCode(zipCode string) *DeleteService { + s.opt_["zipCode"] = zipCode + return s +} + +// Do executes the operation. +func (s *DeleteService) Do(ctx context.Context) (*DeleteResponse, error) { + var body io.Reader + params := make(map[string]interface{}) + if v, ok := s.opt_["region"]; ok { + params["region"] = v + } + params["spn"] = s.spn + if v, ok := s.opt_["zipCode"]; ok { + params["zipCode"] = v + } + path, err := meplatoapi.Expand("/api/v2/products/{spn}/availabilities{?region,zipCode}", params) + if err != nil { + return nil, err + } + req, err := http.NewRequest("DELETE", s.s.BaseURL+path, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set("Accept", "application/json") + req.Header.Set("Accept-Charset", "utf-8") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", meplatoapi.UserAgent) + if s.s.User != "" || s.s.Password != "" { + req.Header.Set("Authorization", meplatoapi.HTTPBasicAuthorizationHeader(s.s.User, s.s.Password)) + } + res, err := s.s.client.Do(req) + if err != nil { + return nil, err + } + defer meplatoapi.CloseBody(res) + if err := meplatoapi.CheckResponse(res); err != nil { + return nil, err + } + ret := new(DeleteResponse) + if err := json.NewDecoder(res.Body).Decode(ret); err != nil { + return nil, err + } + return ret, nil +} + +// Read availability information of a product +type GetService struct { + s *Service + opt_ map[string]interface{} + hdr_ map[string]interface{} + spn string +} + +// NewGetService creates a new instance of GetService. +func NewGetService(s *Service) *GetService { + rs := &GetService{s: s, opt_: make(map[string]interface{}), hdr_: make(map[string]interface{})} + return rs +} + +// 2-letter ISO code of the country/region where the product is stored +func (s *GetService) Region(region string) *GetService { + s.opt_["region"] = region + return s +} + +// SPN is the unique identifier of a product within a merchant. +func (s *GetService) Spn(spn string) *GetService { + s.spn = spn + return s +} + +// Zip code where the product is stored +func (s *GetService) ZipCode(zipCode string) *GetService { + s.opt_["zipCode"] = zipCode + return s +} + +// Do executes the operation. +func (s *GetService) Do(ctx context.Context) (*GetResponse, error) { + var body io.Reader + params := make(map[string]interface{}) + if v, ok := s.opt_["region"]; ok { + params["region"] = v + } + params["spn"] = s.spn + if v, ok := s.opt_["zipCode"]; ok { + params["zipCode"] = v + } + path, err := meplatoapi.Expand("/api/v2/products/{spn}/availabilities{?region,zipCode}", params) + if err != nil { + return nil, err + } + req, err := http.NewRequest("GET", s.s.BaseURL+path, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set("Accept", "application/json") + req.Header.Set("Accept-Charset", "utf-8") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", meplatoapi.UserAgent) + if s.s.User != "" || s.s.Password != "" { + req.Header.Set("Authorization", meplatoapi.HTTPBasicAuthorizationHeader(s.s.User, s.s.Password)) + } + res, err := s.s.client.Do(req) + if err != nil { + return nil, err + } + defer meplatoapi.CloseBody(res) + if err := meplatoapi.CheckResponse(res); err != nil { + return nil, err + } + ret := new(GetResponse) + if err := json.NewDecoder(res.Body).Decode(ret); err != nil { + return nil, err + } + return ret, nil +} + +// Update or create availability information of a product. It is an +// asynchronous operation. +type UpsertService struct { + s *Service + opt_ map[string]interface{} + hdr_ map[string]interface{} + spn string + availability *UpsertRequest +} + +// NewUpsertService creates a new instance of UpsertService. +func NewUpsertService(s *Service) *UpsertService { + rs := &UpsertService{s: s, opt_: make(map[string]interface{}), hdr_: make(map[string]interface{})} + return rs +} + +// Availability properties of the product. +func (s *UpsertService) Availability(availability *UpsertRequest) *UpsertService { + s.availability = availability + return s +} + +// SPN is the unique identifier of a product within a merchant. +func (s *UpsertService) Spn(spn string) *UpsertService { + s.spn = spn + return s +} + +// Do executes the operation. +func (s *UpsertService) Do(ctx context.Context) (*UpsertResponse, error) { + var body io.Reader + body, err := meplatoapi.ReadJSON(s.availability) + if err != nil { + return nil, err + } + params := make(map[string]interface{}) + params["spn"] = s.spn + path, err := meplatoapi.Expand("/api/v2/products/{spn}/availabilities", params) + if err != nil { + return nil, err + } + req, err := http.NewRequest("POST", s.s.BaseURL+path, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + req.Header.Set("Accept", "application/json") + req.Header.Set("Accept-Charset", "utf-8") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", meplatoapi.UserAgent) + if s.s.User != "" || s.s.Password != "" { + req.Header.Set("Authorization", meplatoapi.HTTPBasicAuthorizationHeader(s.s.User, s.s.Password)) + } + res, err := s.s.client.Do(req) + if err != nil { + return nil, err + } + defer meplatoapi.CloseBody(res) + if err := meplatoapi.CheckResponse(res); err != nil { + return nil, err + } + ret := new(UpsertResponse) + if err := json.NewDecoder(res.Body).Decode(ret); err != nil { + return nil, err + } + return ret, nil +} From d5c3d04551f14fb19569fa3dce37932928505d25 Mon Sep 17 00:00:00 2001 From: m-timmermann Date: Mon, 18 Mar 2024 10:25:50 +0100 Subject: [PATCH 2/6] add-availabilities.issue-4041 * remove prefix --- availabilities/availabilities.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/availabilities/availabilities.go b/availabilities/availabilities.go index 0c8601e..8d16f9b 100644 --- a/availabilities/availabilities.go +++ b/availabilities/availabilities.go @@ -191,7 +191,7 @@ func (s *DeleteService) Do(ctx context.Context) (*DeleteResponse, error) { if v, ok := s.opt_["zipCode"]; ok { params["zipCode"] = v } - path, err := meplatoapi.Expand("/api/v2/products/{spn}/availabilities{?region,zipCode}", params) + path, err := meplatoapi.Expand("/products/{spn}/availabilities{?region,zipCode}", params) if err != nil { return nil, err } @@ -265,7 +265,7 @@ func (s *GetService) Do(ctx context.Context) (*GetResponse, error) { if v, ok := s.opt_["zipCode"]; ok { params["zipCode"] = v } - path, err := meplatoapi.Expand("/api/v2/products/{spn}/availabilities{?region,zipCode}", params) + path, err := meplatoapi.Expand("/products/{spn}/availabilities{?region,zipCode}", params) if err != nil { return nil, err } @@ -333,7 +333,7 @@ func (s *UpsertService) Do(ctx context.Context) (*UpsertResponse, error) { } params := make(map[string]interface{}) params["spn"] = s.spn - path, err := meplatoapi.Expand("/api/v2/products/{spn}/availabilities", params) + path, err := meplatoapi.Expand("/products/{spn}/availabilities", params) if err != nil { return nil, err } From ba01543d1affed9bc958a292c271caa59d771d39 Mon Sep 17 00:00:00 2001 From: m-timmermann Date: Mon, 25 Mar 2024 14:47:11 +0100 Subject: [PATCH 3/6] add-availabilities.issue-4041 * fix - change post to put --- availabilities/availabilities.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/availabilities/availabilities.go b/availabilities/availabilities.go index 8d16f9b..9cd2efc 100644 --- a/availabilities/availabilities.go +++ b/availabilities/availabilities.go @@ -337,7 +337,7 @@ func (s *UpsertService) Do(ctx context.Context) (*UpsertResponse, error) { if err != nil { return nil, err } - req, err := http.NewRequest("POST", s.s.BaseURL+path, body) + req, err := http.NewRequest("PUT", s.s.BaseURL+path, body) if err != nil { return nil, err } From f582843f9c98f026e80c4e78883db98ccf9e3670 Mon Sep 17 00:00:00 2001 From: m-timmermann Date: Tue, 26 Mar 2024 12:45:02 +0100 Subject: [PATCH 4/6] add-availabilities.issue-4041 * update workflow --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cff91a..5fa2559 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,18 +10,18 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Unshallow run: git fetch --prune --unshallow - name: Set up Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v5 with: - go-version: 1.18.x + go-version: 1.21 - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v1 + uses: goreleaser/goreleaser-action@v5 with: version: latest args: release --rm-dist From 6d83ddee49a3cfdb6284a632f748d782d225f736 Mon Sep 17 00:00:00 2001 From: m-timmermann Date: Tue, 26 Mar 2024 12:47:37 +0100 Subject: [PATCH 5/6] add-availabilities.issue-4041 * wip - removed key --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5fa2559..42f1dd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,6 +25,5 @@ jobs: with: version: latest args: release --rm-dist - key: ${{ secrets.YOUR_PRIVATE_KEY }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a2e252a0914627bb2b5dbbebd59da9cd9f733d26 Mon Sep 17 00:00:00 2001 From: m-timmermann Date: Thu, 28 Mar 2024 08:13:57 +0100 Subject: [PATCH 6/6] add-availabilities.issue-4041 * add tests --- availabilities/availabilities_test.go | 163 ++++++++++++++++++ availabilities/testdata/.gitattributes | 2 + .../testdata/availabilities.delete.success | 15 ++ .../testdata/availabilities.get.not_found | 17 ++ .../testdata/availabilities.get.success | 45 +++++ .../testdata/availabilities.upsert.success | 16 ++ 6 files changed, 258 insertions(+) create mode 100644 availabilities/availabilities_test.go create mode 100644 availabilities/testdata/.gitattributes create mode 100644 availabilities/testdata/availabilities.delete.success create mode 100644 availabilities/testdata/availabilities.get.not_found create mode 100644 availabilities/testdata/availabilities.get.success create mode 100644 availabilities/testdata/availabilities.upsert.success diff --git a/availabilities/availabilities_test.go b/availabilities/availabilities_test.go new file mode 100644 index 0000000..2fd5c93 --- /dev/null +++ b/availabilities/availabilities_test.go @@ -0,0 +1,163 @@ +package availabilities_test + +import ( + "bufio" + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "path" + "strings" + "testing" + + "github.com/meplato/store2-go-client/v2/availabilities" +) + +func getService(responseFile string) (*availabilities.Service, *httptest.Server, error) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + slurp, err := os.ReadFile(path.Join("testdata", responseFile)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + res, err := http.ReadResponse(bufio.NewReader(strings.NewReader(string(slurp))), r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer res.Body.Close() + bs, err := io.ReadAll(res.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.WriteHeader(res.StatusCode) + fmt.Fprint(w, string(bs)) + })) + + service, err := availabilities.New(http.DefaultClient) + if err != nil { + return service, nil, err + } + service.BaseURL = ts.URL + service.User = os.Getenv("STORE2_USER") + service.Password = os.Getenv("STORE2_PASSWORD") + return service, ts, nil +} + +func TestAvailabilitiesGet(t *testing.T) { + service, ts, err := getService("availabilities.get.success") + if err != nil { + t.Fatal(err) + } + if service == nil { + t.Fatal("expected service; got: nil") + } + defer ts.Close() + + res, err := service.Get().Spn("1234").Do(context.Background()) + if err != nil { + t.Fatal(err) + } + if res == nil { + t.Fatal("expected response; got: nil") + } + + if len(res.Items) != 3 { + t.Fatalf("expected items to be of length 3; got: %d", len(res.Items)) + } + + if res.Kind != "store#availabilities/getResponse" { + t.Fatalf("expected kind %q; got: %v", "store#availabilities/getResponse", res.Kind) + } + firstItem := res.Items[0] + + if firstItem == nil { + t.Fatal("expected first entry; got: nil") + } else { + if firstItem.Spn != "1234" { + t.Fatalf("expected availability Spn %v; got: %v", "1234", firstItem.Spn) + } + } +} +func TestAvailabilitiesGetNotFound(t *testing.T) { + service, ts, err := getService("availabilities.get.not_found") + if err != nil { + t.Fatal(err) + } + if service == nil { + t.Fatal("expected service; got: nil") + } + defer ts.Close() + + res, err := service.Get().Spn("1235").Do(context.Background()) + if err != nil { + t.Fatal(err) + } + if res == nil { + t.Fatal("expected response; got: nil") + } + + if len(res.Items) != 0 { + t.Fatalf("expected items to be of length 0; got: %d", len(res.Items)) + } + + if res.Kind != "store#availabilities/getResponse" { + t.Fatalf("expected kind %q; got: %v", "store#availabilities/getResponse", res.Kind) + } +} +func TestAvailabilitiesUpsert(t *testing.T) { + service, ts, err := getService("availabilities.upsert.success") + if err != nil { + t.Fatal(err) + } + if service == nil { + t.Fatal("expected service; got: nil") + } + defer ts.Close() + + var Quantity = 0.0 + + res, err := service.Upsert().Spn("1234").Availability(&availabilities.UpsertRequest{ + Message: "not in stock", + Quantity: &Quantity, + Region: "AQ", + Updated: "Q1/2024", + ZipCode: "1234", + }).Do(context.Background()) + if err != nil { + t.Fatal(err) + } + if res == nil { + t.Fatal("expected response; got: nil") + } + + if res.Kind != "store#availabilities/upsertResponse" { + t.Fatalf("expected kind %q; got: %v", "store#availabilities/upsertResponse", res.Kind) + } +} + +func TestAvailabilitiesDelete(t *testing.T) { + service, ts, err := getService("availabilities.delete.success") + if err != nil { + t.Fatal(err) + } + if service == nil { + t.Fatal("expected service; got: nil") + } + defer ts.Close() + + res, err := service.Delete().Spn("1234").Region("DE").ZipCode("12345").Do(context.Background()) + if err != nil { + t.Fatal(err) + } + if res == nil { + t.Fatal("expected response; got: nil") + } + + if res.Kind != "store#availabilities/deleteResponse" { + t.Fatalf("expected kind %q; got: %v", "store#availabilities/deleteResponse", res.Kind) + } +} diff --git a/availabilities/testdata/.gitattributes b/availabilities/testdata/.gitattributes new file mode 100644 index 0000000..b32ffa1 --- /dev/null +++ b/availabilities/testdata/.gitattributes @@ -0,0 +1,2 @@ +* eol=crlf linguist-vendored + diff --git a/availabilities/testdata/availabilities.delete.success b/availabilities/testdata/availabilities.delete.success new file mode 100644 index 0000000..e0ebfc2 --- /dev/null +++ b/availabilities/testdata/availabilities.delete.success @@ -0,0 +1,15 @@ +HTTP/1.1 202 OK +Cache-Control: private, no-cache +Content-Type: application/json; charset=utf-8 +Last-Modified: Tue, 31 Mar 2015 14:18:15 GMT +P3p: CP="This is not a P3P policy!" +Vary: Cookie +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Ua-Compatible: IE=edge +X-Xss-Protection: 1; mode=block +Date: Tue, 25 Mar 2024 14:18:15 GMT + +{ + "kind": "store#availabilities/deleteResponse" +} \ No newline at end of file diff --git a/availabilities/testdata/availabilities.get.not_found b/availabilities/testdata/availabilities.get.not_found new file mode 100644 index 0000000..004094c --- /dev/null +++ b/availabilities/testdata/availabilities.get.not_found @@ -0,0 +1,17 @@ +HTTP/1.1 200 +Cache-Control: private, no-cache +Content-Type: application/json; charset=utf-8 +Last-Modified: Tue, 31 Mar 2015 14:15:25 GMT +P3p: CP="This is not a P3P policy!" +Vary: Cookie +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Ua-Compatible: IE=edge +X-Xss-Protection: 1; mode=block +Date: Tue, 31 Mar 2015 14:15:25 GMT + +{ + "kind": "store#availabilities/getResponse", + "items": null, + "Error": null +} diff --git a/availabilities/testdata/availabilities.get.success b/availabilities/testdata/availabilities.get.success new file mode 100644 index 0000000..5a77f9e --- /dev/null +++ b/availabilities/testdata/availabilities.get.success @@ -0,0 +1,45 @@ +HTTP/1.1 200 OK +Cache-Control: private, no-cache +Content-Type: application/json; charset=utf-8 +Last-Modified: Tue, 31 Mar 2015 14:18:15 GMT +P3p: CP="This is not a P3P policy!" +Vary: Cookie +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Ua-Compatible: IE=edge +X-Xss-Protection: 1; mode=block +Date: Tue, 25 Mar 2024 14:18:15 GMT + +{ + "kind": "store#availabilities/getResponse", + "Error": null, + "items": [ + { + "message": "in stock", + "mpcc": "meplato", + "quantity": 15.3, + "region": "UK", + "spn": "1234", + "updated": "Q4/2022", + "zipCode": "04109" + }, + { + "message": "in stock", + "mpcc": "meplato", + "quantity": 35.0, + "region": "DK", + "spn": "1234", + "updated": "Q4/2022", + "zipCode": "05109" + }, + { + "message": "in stock", + "mpcc": "meplato", + "quantity": 20.4, + "region": "DE", + "spn": "1234", + "updated": "Q4/2022", + "zipCode": "06109" + } + ] +} \ No newline at end of file diff --git a/availabilities/testdata/availabilities.upsert.success b/availabilities/testdata/availabilities.upsert.success new file mode 100644 index 0000000..9590369 --- /dev/null +++ b/availabilities/testdata/availabilities.upsert.success @@ -0,0 +1,16 @@ +HTTP/1.1 202 OK +Cache-Control: private, no-cache +Content-Type: application/json; charset=utf-8 +Last-Modified: Tue, 31 Mar 2015 14:18:15 GMT +P3p: CP="This is not a P3P policy!" +Vary: Cookie +X-Content-Type-Options: nosniff +X-Frame-Options: SAMEORIGIN +X-Ua-Compatible: IE=edge +X-Xss-Protection: 1; mode=block +Date: Tue, 25 Mar 2024 14:18:15 GMT + +{ + "kind": "store#availabilities/upsertResponse", + "link": "string" +} \ No newline at end of file