From fe76cf148da3adddf9d4fa58a3317e608f35f500 Mon Sep 17 00:00:00 2001 From: Adam Williams Date: Wed, 5 Oct 2022 20:23:39 +0200 Subject: [PATCH] Add Secret Store API support (#367) Secret Store is currently part of a beta release. https://developer.fastly.com/reference/api/secret-store/ --- fastly/errors.go | 31 +- fastly/errors_test.go | 40 +- .../secret_store/TestClient_CreateSecret.yaml | 40 ++ .../create_store_00.yaml | 40 ++ .../delete_store_00.yaml | 31 ++ .../TestClient_CreateSecretStore.yaml | 40 ++ .../delete_store.yaml | 31 ++ .../secret_store/TestClient_DeleteSecret.yaml | 31 ++ .../create_secret.yaml | 40 ++ .../create_store_00.yaml | 40 ++ .../delete_store_00.yaml | 31 ++ .../TestClient_DeleteSecretStore.yaml | 31 ++ .../create_store.yaml | 40 ++ .../secret_store/TestClient_GetSecret.yaml | 39 ++ .../TestClient_GetSecret/create_secret.yaml | 40 ++ .../TestClient_GetSecret/create_store_00.yaml | 40 ++ .../TestClient_GetSecret/delete_store_00.yaml | 31 ++ .../TestClient_GetSecretStore.yaml | 39 ++ .../create_store_00.yaml | 40 ++ .../delete_store_00.yaml | 31 ++ .../TestClient_ListSecretStores.yaml | 62 +++ .../create_store_00.yaml | 40 ++ .../create_store_01.yaml | 40 ++ .../create_store_02.yaml | 40 ++ .../create_store_03.yaml | 40 ++ .../create_store_04.yaml | 40 ++ .../delete_store_00.yaml | 31 ++ .../delete_store_01.yaml | 31 ++ .../delete_store_02.yaml | 31 ++ .../delete_store_03.yaml | 31 ++ .../delete_store_04.yaml | 31 ++ .../secret_store/TestClient_ListSecrets.yaml | 62 +++ .../create_secret_00.yaml | 40 ++ .../create_secret_01.yaml | 40 ++ .../create_secret_02.yaml | 40 ++ .../create_secret_03.yaml | 40 ++ .../create_secret_04.yaml | 40 ++ .../create_store_00.yaml | 40 ++ .../delete_store_00.yaml | 31 ++ fastly/secret_store.go | 377 ++++++++++++++++ fastly/secret_store_test.go | 420 ++++++++++++++++++ 41 files changed, 2266 insertions(+), 7 deletions(-) create mode 100644 fastly/fixtures/secret_store/TestClient_CreateSecret.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_CreateSecret/create_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_CreateSecret/delete_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_CreateSecretStore.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_CreateSecretStore/delete_store.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_DeleteSecret.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_DeleteSecret/create_secret.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_DeleteSecret/create_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_DeleteSecret/delete_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_DeleteSecretStore.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_DeleteSecretStore/create_store.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecret.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecret/create_secret.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecret/create_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecret/delete_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecretStore.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecretStore/create_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_GetSecretStore/delete_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_01.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_02.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_03.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_04.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_01.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_02.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_03.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_04.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_01.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_02.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_03.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_04.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/create_store_00.yaml create mode 100644 fastly/fixtures/secret_store/TestClient_ListSecrets/delete_store_00.yaml create mode 100644 fastly/secret_store.go create mode 100644 fastly/secret_store_test.go diff --git a/fastly/errors.go b/fastly/errors.go index f8835c4ad..07a0f6945 100644 --- a/fastly/errors.go +++ b/fastly/errors.go @@ -2,9 +2,11 @@ package fastly import ( "bytes" + "encoding/json" "errors" "fmt" "net/http" + "strconv" "github.com/google/jsonapi" ) @@ -165,6 +167,10 @@ var ErrMissingNumber = NewFieldError("Number") // requires a "PoolID" key, but one was not set. var ErrMissingPoolID = NewFieldError("PoolID") +// ErrMissingSecret is an error that is returned when an input struct +// requires a "Secret" key, but one was not set. +var ErrMissingSecret = NewFieldError("Secret") + // ErrMissingServer is an error that is returned when an input struct // requires a "Server" key, but one was not set. var ErrMissingServer = NewFieldError("Server") @@ -318,12 +324,31 @@ func NewHTTPError(resp *http.Response) *HTTPError { return &e } - // If this is a jsonapi response, decode it accordingly - if resp.Header.Get("Content-Type") == jsonapi.MediaType { + switch resp.Header.Get("Content-Type") { + case jsonapi.MediaType: + // If this is a jsonapi response, decode it accordingly if err := decodeBodyMap(resp.Body, &e); err != nil { panic(err) } - } else { + + case "application/problem+json": + // Response is a "problem detail" as defined in RFC 7807. + var problemDetail struct { + URL string `json:"type,omitempty"` // URL to a human-readable document describing this specific error condition + Title string `json:"title,omitempty"` // A short name for the error type, which remains constant from occurrence to occurrence + Status int `json:"status"` // HTTP status code + Detail string `json:"detail,omitempty"` // A human-readable description of the specific error, aiming to help the user correct the problem + } + if err := json.NewDecoder(resp.Body).Decode(&problemDetail); err != nil { + panic(err) + } + e.Errors = append(e.Errors, &ErrorObject{ + Title: problemDetail.Title, + Detail: problemDetail.Detail, + Status: strconv.Itoa(problemDetail.Status), + }) + + default: var lerr *legacyError decodeBodyMap(resp.Body, &lerr) if lerr != nil { diff --git a/fastly/errors_test.go b/fastly/errors_test.go index e6e40035b..e60b13446 100644 --- a/fastly/errors_test.go +++ b/fastly/errors_test.go @@ -32,10 +32,10 @@ func TestNewHTTPError(t *testing.T) { Detail: nope `) if e.Error() != expected { - t.Errorf("expected \n\n%s\n\n to be \n\n%s\n\n", e.Error(), expected) + t.Errorf("expected \n\n%q\n\n to be \n\n%q\n\n", e.Error(), expected) } if e.String() != expected { - t.Errorf("expected \n\n%s\n\n to be \n\n%s\n\n", e.String(), expected) + t.Errorf("expected \n\n%q\n\n to be \n\n%q\n\n", e.String(), expected) } if !e.IsNotFound() { @@ -64,10 +64,42 @@ func TestNewHTTPError(t *testing.T) { Detail: That resource does not exist `) if e.Error() != expected { - t.Errorf("expected \n\n%s\n\n to be \n\n%s\n\n", e.Error(), expected) + t.Errorf("expected \n\n%q\n\n to be \n\n%q\n\n", e.Error(), expected) } if e.String() != expected { - t.Errorf("expected \n\n%s\n\n to be \n\n%s\n\n", e.String(), expected) + t.Errorf("expected \n\n%q\n\n to be \n\n%q\n\n", e.String(), expected) + } + + if !e.IsNotFound() { + t.Error("not not found") + } + }) + + t.Run("problem detail", func(t *testing.T) { + resp := &http.Response{ + StatusCode: 404, + Header: http.Header(map[string][]string{"Content-Type": {"application/problem+json"}}), + Body: io.NopCloser(bytes.NewBufferString( + `{"title": "Error", "detail": "this was an error", "status": 404}`, + )), + } + e := NewHTTPError(resp) + + if e.StatusCode != 404 { + t.Errorf("expected %d to be %d", e.StatusCode, 404) + } + + expected := strings.TrimSpace(` +404 - Not Found: + + Title: Error + Detail: this was an error +`) + if e.Error() != expected { + t.Errorf("expected \n\n%q\n\n to be \n\n%q\n\n", e.Error(), expected) + } + if e.String() != expected { + t.Errorf("expected \n\n%q\n\n to be \n\n%q\n\n", e.String(), expected) } if !e.IsNotFound() { diff --git a/fastly/fixtures/secret_store/TestClient_CreateSecret.yaml b/fastly/fixtures/secret_store/TestClient_CreateSecret.yaml new file mode 100644 index 000000000..d8b6ff20f --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_CreateSecret.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_CreateSecret","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/YsFsqmjSDvg52GhvBeIbqw/secrets + method: POST + response: + body: | + { + "name": "TestClient_CreateSecret", + "digest": "NLdzRaW6NfFy6RS3yPUVk7ZtK2oiIeCRQVN0sXR2564=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "100" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - 73bYYj5weXQGItmRssm4Qm + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_CreateSecret/create_store_00.yaml b/fastly/fixtures/secret_store/TestClient_CreateSecret/create_store_00.yaml new file mode 100644 index 000000000..09cd2f222 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_CreateSecret/create_store_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_CreateSecret-00"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "YsFsqmjSDvg52GhvBeIbqw", + "name": "TestClient_CreateSecret-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "77" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - lubT76EDRYTxymsIx3TJGg + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_CreateSecret/delete_store_00.yaml b/fastly/fixtures/secret_store/TestClient_CreateSecret/delete_store_00.yaml new file mode 100644 index 000000000..5bde6bb6d --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_CreateSecret/delete_store_00.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/YsFsqmjSDvg52GhvBeIbqw + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - eProXX1wiDOdHLjqcvCxSq + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_CreateSecretStore.yaml b/fastly/fixtures/secret_store/TestClient_CreateSecretStore.yaml new file mode 100644 index 000000000..de2ac9f2c --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_CreateSecretStore.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_CreateSecretStore"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "qIbYRt3b3mzvSTFVONM2uo", + "name": "TestClient_CreateSecretStore" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "79" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - AFNpFkYCDNQu7cj4AJCEzL + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_CreateSecretStore/delete_store.yaml b/fastly/fixtures/secret_store/TestClient_CreateSecretStore/delete_store.yaml new file mode 100644 index 000000000..a0f860d60 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_CreateSecretStore/delete_store.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/qIbYRt3b3mzvSTFVONM2uo + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - ZbnCNVaCnA5VNvYp49wS5y + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_DeleteSecret.yaml b/fastly/fixtures/secret_store/TestClient_DeleteSecret.yaml new file mode 100644 index 000000000..99a36b129 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_DeleteSecret.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/DwDUfumJxzsKYVBhXNUT4m/secrets/TestClient_DeleteSecret + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - IGREzUCNGXsWnFKbh38kCk + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_DeleteSecret/create_secret.yaml b/fastly/fixtures/secret_store/TestClient_DeleteSecret/create_secret.yaml new file mode 100644 index 000000000..8c2e68772 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_DeleteSecret/create_secret.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_DeleteSecret","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/DwDUfumJxzsKYVBhXNUT4m/secrets + method: POST + response: + body: | + { + "name": "TestClient_DeleteSecret", + "digest": "EjvFk6TSfhAMXrLFzCLsAJYqLs8BwBM5PSMa7rJCNBk=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "100" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - diMVjRZ4rKGIheD64pCLd2 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_DeleteSecret/create_store_00.yaml b/fastly/fixtures/secret_store/TestClient_DeleteSecret/create_store_00.yaml new file mode 100644 index 000000000..03eec28a0 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_DeleteSecret/create_store_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_DeleteSecret-00"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "DwDUfumJxzsKYVBhXNUT4m", + "name": "TestClient_DeleteSecret-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "77" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - jeaubLpg5KVLtk64c5HOCc + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_DeleteSecret/delete_store_00.yaml b/fastly/fixtures/secret_store/TestClient_DeleteSecret/delete_store_00.yaml new file mode 100644 index 000000000..758ab23fe --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_DeleteSecret/delete_store_00.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/DwDUfumJxzsKYVBhXNUT4m + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - aHSOIGhJNftUqijTHwiP2Z + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_DeleteSecretStore.yaml b/fastly/fixtures/secret_store/TestClient_DeleteSecretStore.yaml new file mode 100644 index 000000000..34dda7417 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_DeleteSecretStore.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/38BjRL0jFRvujBb9ksPp9r + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - ZeiOjchZtmXPBHIpPWzXeN + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_DeleteSecretStore/create_store.yaml b/fastly/fixtures/secret_store/TestClient_DeleteSecretStore/create_store.yaml new file mode 100644 index 000000000..d48f2b2be --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_DeleteSecretStore/create_store.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_DeleteSecretStore"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "38BjRL0jFRvujBb9ksPp9r", + "name": "TestClient_DeleteSecretStore" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "79" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - x4k7xNewTPEAEkJZhI49A4 + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecret.yaml b/fastly/fixtures/secret_store/TestClient_GetSecret.yaml new file mode 100644 index 000000000..35925d8ab --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecret.yaml @@ -0,0 +1,39 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/jGzF8tLn5RfTD04ARxwYEl/secrets/TestClient_GetSecret + method: GET + response: + body: | + { + "name": "TestClient_GetSecret", + "digest": "v8AbBYi0rwsk9TryF4pkGiJRqQqCBrV58eEj+geNtcg=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "97" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - kKKxiOWQZdSoBACNEWMddw + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecret/create_secret.yaml b/fastly/fixtures/secret_store/TestClient_GetSecret/create_secret.yaml new file mode 100644 index 000000000..046b9fc6e --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecret/create_secret.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_GetSecret","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/jGzF8tLn5RfTD04ARxwYEl/secrets + method: POST + response: + body: | + { + "name": "TestClient_GetSecret", + "digest": "v8AbBYi0rwsk9TryF4pkGiJRqQqCBrV58eEj+geNtcg=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "97" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - vjCSKeYxxP9HFRwlovBzWW + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecret/create_store_00.yaml b/fastly/fixtures/secret_store/TestClient_GetSecret/create_store_00.yaml new file mode 100644 index 000000000..de4c1f20e --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecret/create_store_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_GetSecret-00"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "jGzF8tLn5RfTD04ARxwYEl", + "name": "TestClient_GetSecret-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "74" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - wqNQKuZ4VuLYO47WgXlvxa + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecret/delete_store_00.yaml b/fastly/fixtures/secret_store/TestClient_GetSecret/delete_store_00.yaml new file mode 100644 index 000000000..82b178294 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecret/delete_store_00.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/jGzF8tLn5RfTD04ARxwYEl + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - rOAWxwNv71IxYnsrDVlFM6 + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecretStore.yaml b/fastly/fixtures/secret_store/TestClient_GetSecretStore.yaml new file mode 100644 index 000000000..3e8e71413 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecretStore.yaml @@ -0,0 +1,39 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/T3WCZ0BEMTeah773LEMuXf + method: GET + response: + body: | + { + "id": "T3WCZ0BEMTeah773LEMuXf", + "name": "TestClient_GetSecretStore-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "79" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - GOL2WEGePXKGlUtGhk2Xyd + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecretStore/create_store_00.yaml b/fastly/fixtures/secret_store/TestClient_GetSecretStore/create_store_00.yaml new file mode 100644 index 000000000..73d3065f6 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecretStore/create_store_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_GetSecretStore-00"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "T3WCZ0BEMTeah773LEMuXf", + "name": "TestClient_GetSecretStore-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "79" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - Wc2yuXbywn2kXgJ9noSbiQ + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_GetSecretStore/delete_store_00.yaml b/fastly/fixtures/secret_store/TestClient_GetSecretStore/delete_store_00.yaml new file mode 100644 index 000000000..eabcb639e --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_GetSecretStore/delete_store_00.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/T3WCZ0BEMTeah773LEMuXf + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - GzAAMoQTVFh9BjX6afF0C8 + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores.yaml new file mode 100644 index 000000000..418b4370e --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores.yaml @@ -0,0 +1,62 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: GET + response: + body: | + { + "data": [ + { + "id": "Svutc59lw0v85xFhQVmK01", + "name": "TestClient_ListSecretStores-03" + }, + { + "id": "eKvDJXrsMTMINiapvCUJZe", + "name": "TestClient_ListSecretStores-01" + }, + { + "id": "geskNox0MimbeFmpCo2y4S", + "name": "TestClient_ListSecretStores-00" + }, + { + "id": "imA8j50hMdlbEhreM2igNP", + "name": "TestClient_ListSecretStores-02" + }, + { + "id": "wzLBnkxQV291CX4unAYefO", + "name": "TestClient_ListSecretStores-04" + } + ], + "meta": { + "limit": 10 + } + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "542" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - zuKKhnirBmGWSdcLoQDUJV + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_00.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_00.yaml new file mode 100644 index 000000000..1dc5897f6 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecretStores-00"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "geskNox0MimbeFmpCo2y4S", + "name": "TestClient_ListSecretStores-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "81" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - mrAfeG4gX8uEk4xPsAl4O8 + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_01.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_01.yaml new file mode 100644 index 000000000..05d04ab5b --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_01.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecretStores-01"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "eKvDJXrsMTMINiapvCUJZe", + "name": "TestClient_ListSecretStores-01" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "81" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - 8BsKTuBRdvJpvABwvApMI6 + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_02.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_02.yaml new file mode 100644 index 000000000..a62b400a9 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_02.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecretStores-02"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "imA8j50hMdlbEhreM2igNP", + "name": "TestClient_ListSecretStores-02" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "81" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - LOxZ0eaIgmJcWxhVxQcNi4 + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_03.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_03.yaml new file mode 100644 index 000000000..5aaac5cf5 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_03.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecretStores-03"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "Svutc59lw0v85xFhQVmK01", + "name": "TestClient_ListSecretStores-03" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "81" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - MlGFIsssipvaw2yHerPPsl + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_04.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_04.yaml new file mode 100644 index 000000000..e55740320 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/create_store_04.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecretStores-04"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "wzLBnkxQV291CX4unAYefO", + "name": "TestClient_ListSecretStores-04" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "81" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - JSj7ZWXwnk3wS1CtFWw3Rv + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_00.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_00.yaml new file mode 100644 index 000000000..1bd285935 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_00.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/geskNox0MimbeFmpCo2y4S + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - VsVKbbJRA4lHIoT5jUMJG9 + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_01.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_01.yaml new file mode 100644 index 000000000..375f1c3fa --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_01.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/eKvDJXrsMTMINiapvCUJZe + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - pWOL5nFrWkCjStr3lFd1TI + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_02.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_02.yaml new file mode 100644 index 000000000..0f4a800de --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_02.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/imA8j50hMdlbEhreM2igNP + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - F5Vux4JnXJqoeZpXi5ictw + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_03.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_03.yaml new file mode 100644 index 000000000..81a87def1 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_03.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/Svutc59lw0v85xFhQVmK01 + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - Xserpm2298nXi7dN84u2mS + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_04.yaml b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_04.yaml new file mode 100644 index 000000000..4e124a5fc --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecretStores/delete_store_04.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/wzLBnkxQV291CX4unAYefO + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:01 GMT + Fastly-Trace-Id: + - H7Nj9Iko7x6z3IUm3Jos0Y + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets.yaml new file mode 100644 index 000000000..e0f6b1138 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets.yaml @@ -0,0 +1,62 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq/secrets + method: GET + response: + body: | + { + "data": [ + { + "name": "TestClient_ListSecrets-00", + "digest": "hHC5cATutIsYicNSPlBUzLfd0aGWKoPi1UG8MDuQpps=" + }, + { + "name": "TestClient_ListSecrets-01", + "digest": "yxMEqKry7Wz9mFKw4el2a0qTaWJT4VUa5+uuUgMnkCY=" + }, + { + "name": "TestClient_ListSecrets-04", + "digest": "/gtTi221CmscCnVrcd9SdrxrHEdHMSrtfPibWxlgblM=" + }, + { + "name": "TestClient_ListSecrets-03", + "digest": "qJWQJ48jF9gO+FGl/Asw6fCsT3wBb3BvG+7gTnQmAmk=" + }, + { + "name": "TestClient_ListSecrets-02", + "digest": "iyaN1TnCEFUjE3XAy0o/SNQLeAO2dKepT3rUlLP3j4g=" + } + ], + "meta": { + "limit": 10 + } + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "647" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - cJfIQSbwZpVSvXkuCClA97 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_00.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_00.yaml new file mode 100644 index 000000000..d3f36efca --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecrets-00","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq/secrets + method: POST + response: + body: | + { + "name": "TestClient_ListSecrets-00", + "digest": "hHC5cATutIsYicNSPlBUzLfd0aGWKoPi1UG8MDuQpps=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "102" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - KTefv8ESZIodyshVjmOktQ + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_01.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_01.yaml new file mode 100644 index 000000000..d1bcb14be --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_01.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecrets-01","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq/secrets + method: POST + response: + body: | + { + "name": "TestClient_ListSecrets-01", + "digest": "yxMEqKry7Wz9mFKw4el2a0qTaWJT4VUa5+uuUgMnkCY=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "102" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - aVsHueYO66VYxrRBbXVfhc + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_02.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_02.yaml new file mode 100644 index 000000000..19c68dc6f --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_02.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecrets-02","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq/secrets + method: POST + response: + body: | + { + "name": "TestClient_ListSecrets-02", + "digest": "iyaN1TnCEFUjE3XAy0o/SNQLeAO2dKepT3rUlLP3j4g=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "102" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - 1lbzJ7N8ArsZFpGbQazPjL + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_03.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_03.yaml new file mode 100644 index 000000000..16186d577 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_03.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecrets-03","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq/secrets + method: POST + response: + body: | + { + "name": "TestClient_ListSecrets-03", + "digest": "qJWQJ48jF9gO+FGl/Asw6fCsT3wBb3BvG+7gTnQmAmk=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "102" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - mItuF4ph5ZLGI8E6g490dy + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_04.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_04.yaml new file mode 100644 index 000000000..cc8bf07d6 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_secret_04.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecrets-04","secret":"c2VjcmV0dW0gc2VydmFyZQ=="} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq/secrets + method: POST + response: + body: | + { + "name": "TestClient_ListSecrets-04", + "digest": "/gtTi221CmscCnVrcd9SdrxrHEdHMSrtfPibWxlgblM=" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "102" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - kVpoL44aZgfH4qhp97me3q + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/create_store_00.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_store_00.yaml new file mode 100644 index 000000000..55e6779c7 --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/create_store_00.yaml @@ -0,0 +1,40 @@ +--- +version: 1 +interactions: +- request: + body: | + {"name":"TestClient_ListSecrets-00"} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret + method: POST + response: + body: | + { + "id": "crO6dvsiLjNyGMItdA8Duq", + "name": "TestClient_ListSecrets-00" + } + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Content-Length: + - "76" + Content-Type: + - application/json + Date: + - Fri, 23 Sep 2022 16:09:02 GMT + Fastly-Trace-Id: + - MGYOutDtTLkEw1h76k8Roq + status: 201 Created + code: 201 + duration: "" diff --git a/fastly/fixtures/secret_store/TestClient_ListSecrets/delete_store_00.yaml b/fastly/fixtures/secret_store/TestClient_ListSecrets/delete_store_00.yaml new file mode 100644 index 000000000..e4504220e --- /dev/null +++ b/fastly/fixtures/secret_store/TestClient_ListSecrets/delete_store_00.yaml @@ -0,0 +1,31 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - FastlyGo/6.5.2 (+github.com/fastly/go-fastly; go1.19.1) + url: https://api.fastly.com/resources/stores/secret/crO6dvsiLjNyGMItdA8Duq + method: DELETE + response: + body: "" + headers: + Access-Control-Allow-Headers: + - Content-Type, Fastly-Key + Access-Control-Allow-Methods: + - PUT, POST, GET, OPTIONS, DELETE + Access-Control-Allow-Origin: + - '*' + Date: + - Fri, 23 Sep 2022 16:09:03 GMT + Fastly-Trace-Id: + - wPsv4SN0GcmovB0VLnwEJX + status: 204 No Content + code: 204 + duration: "" diff --git a/fastly/secret_store.go b/fastly/secret_store.go new file mode 100644 index 000000000..bccd9d785 --- /dev/null +++ b/fastly/secret_store.go @@ -0,0 +1,377 @@ +package fastly + +import ( + "bytes" + "encoding/json" + "strconv" +) + +// Secret Store. +// A secret store is a persistent, globally distributed store for secrets accessible to Compute@Edge services during request processing. +// https://developer.fastly.com/reference/api/secret-store/ + +// SecretStoreMeta is the metadata returned from Secret Store paginated responses. +type SecretStoreMeta struct { + // Limit is the limit of results returned. + Limit int `json:"limit"` + // NextCursor can be used on a subsequent request to fetch + // the next page of data. + NextCursor string `json:"next_cursor,omitempty"` +} + +// SecretStore represents a Secret Store response from the Fastly API. +type SecretStore struct { + ID string `json:"id"` + Name string `json:"name"` +} + +// CreateSecretStoreInput is used as input to the CreateSecretStore function. +type CreateSecretStoreInput struct { + // Name of the Secret Store (required). + Name string +} + +// CreateSecretStore creates a new Secret Store. +func (c *Client) CreateSecretStore(i *CreateSecretStoreInput) (*SecretStore, error) { + if i.Name == "" { + return nil, ErrMissingName + } + + p := "/resources/stores/secret" + + var body bytes.Buffer + err := json.NewEncoder(&body).Encode(struct { + Name string `json:"name"` + }{ + Name: i.Name, + }) + if err != nil { + return nil, err + } + + resp, err := c.Post(p, &RequestOptions{ + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Body: &body, + Parallel: true, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var output SecretStore + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return nil, err + } + + return &output, nil +} + +// SecretStores represents a list of Secret Stores from the Fastly API. +type SecretStores struct { + // Data is a list of Secret Stores. + Data []SecretStore `json:"data"` + // Meta contains response pagination data. + Meta SecretStoreMeta `json:"meta"` +} + +// ListSecretStoresInput is used as input to the ListSecretStores function. +type ListSecretStoresInput struct { + // Limit is the desired number of Secret Stores (optional). + Limit int + // Cursor is the pagination cursor (optional). + Cursor string +} + +// ListSecretStores returns a paginated list of Secret Stores. +// The returned next cursor, if non-blank, can be used as input to a subsequent +// request for the next page of results. +func (c *Client) ListSecretStores(i *ListSecretStoresInput) (*SecretStores, error) { + p := "/resources/stores/secret" + + params := make(map[string]string, 2) + if i.Limit > 0 { + params["limit"] = strconv.Itoa(i.Limit) + } + if i.Cursor != "" { + params["cursor"] = i.Cursor + } + + resp, err := c.Get(p, &RequestOptions{ + Params: params, + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Parallel: true, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var output SecretStores + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return nil, err + } + + return &output, nil +} + +// GetSecretStoreInput is used as input to the GetSecretStore function. +type GetSecretStoreInput struct { + // ID of the Secret Store (required). + ID string +} + +// GetSecretStore gets a single Secret Store. +func (c *Client) GetSecretStore(i *GetSecretStoreInput) (*SecretStore, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + p := "/resources/stores/secret/" + i.ID + + resp, err := c.Get(p, &RequestOptions{ + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Parallel: true, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var output SecretStore + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return nil, err + } + + return &output, nil +} + +// DeleteSecretStoreInput is used as input to the DeleteSecretStore function. +type DeleteSecretStoreInput struct { + // ID of the Secret Store (required). + ID string +} + +// DeleteSecretStore deletes the given Secret Store and associated Secrets. +func (c *Client) DeleteSecretStore(i *DeleteSecretStoreInput) error { + if i.ID == "" { + return ErrMissingID + } + + p := "/resources/stores/secret/" + i.ID + + resp, err := c.Delete(p, &RequestOptions{ + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Parallel: true, + }) + if err != nil { + return err + } + return resp.Body.Close() +} + +// Secret is a Secret Store secret. +type Secret struct { + Name string `json:"name"` + // Digest is an opaque hash of the secret. + Digest []byte `json:"digest"` +} + +// CreateSecretInput is used as input to the CreateSecret function. +type CreateSecretInput struct { + // ID of the Secret Store (required). + ID string + // Name of the Secret (required). + Name string + // Secret is the plaintext secret to be stored (required). + // The value will be base64-encoded when delivered to the API, + // which is the required format. + Secret []byte +} + +// CreateSecret creates a new Secret within a Secret Store. +func (c *Client) CreateSecret(i *CreateSecretInput) (*Secret, error) { + if i.ID == "" { + return nil, ErrMissingID + } + if i.Name == "" { + return nil, ErrMissingName + } + if len(i.Secret) == 0 { + return nil, ErrMissingSecret + } + + p := "/resources/stores/secret/" + i.ID + "/secrets" + + var body bytes.Buffer + err := json.NewEncoder(&body).Encode(struct { + Name string `json:"name"` + Secret []byte `json:"secret"` + }{ + Name: i.Name, + Secret: i.Secret, + }) + if err != nil { + return nil, err + } + + resp, err := c.Post(p, &RequestOptions{ + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Body: &body, + Parallel: true, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var output Secret + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return nil, err + } + + return &output, nil +} + +// Secrets represents a list of Secrets from the Fastly API. +type Secrets struct { + // Data is a list of Secrets. + Data []Secret `json:"data"` + // Meta contains pagination data. + Meta SecretStoreMeta `json:"meta"` +} + +// ListSecretsInput is used as input to the ListSecrets function. +type ListSecretsInput struct { + // ID of the Secret Store (required). + ID string + // Limit is the desired number of Secrets (optional). + Limit int + // Cursor is the pagination cursor (optional). + Cursor string +} + +// ListSecrets returns a list of Secrets for the given Secret Store. +// The returned next cursor, if non-blank, can be used as input to a subsequent +// request for the next page of results. +func (c *Client) ListSecrets(i *ListSecretsInput) (*Secrets, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + p := "/resources/stores/secret/" + i.ID + "/secrets" + + params := make(map[string]string, 2) + if i.Limit > 0 { + params["limit"] = strconv.Itoa(i.Limit) + } + if i.Cursor != "" { + params["cursor"] = i.Cursor + } + + resp, err := c.Get(p, &RequestOptions{ + Params: params, + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Parallel: true, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var output Secrets + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return nil, err + } + + return &output, nil +} + +// GetSecretInput is used as input to the GetSecret function. +type GetSecretInput struct { + // ID of the Secret Store (required). + ID string + // Name of the Secret (required). + Name string +} + +// GetSecret returns a single Secret from a given Secret Store. +func (c *Client) GetSecret(i *GetSecretInput) (*Secret, error) { + if i.ID == "" { + return nil, ErrMissingID + } + if i.Name == "" { + return nil, ErrMissingName + } + + p := "/resources/stores/secret/" + i.ID + "/secrets/" + i.Name + + resp, err := c.Get(p, &RequestOptions{ + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Parallel: true, + }) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var output Secret + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return nil, err + } + + return &output, nil +} + +// DeleteSecretInput is used as input to the DeleteSecret function. +type DeleteSecretInput struct { + // ID of the Secret Store (required). + ID string + // Name of the secret (required). + Name string +} + +// DeleteSecret deletes the given Secret. +func (c *Client) DeleteSecret(i *DeleteSecretInput) error { + if i.ID == "" { + return ErrMissingID + } + if i.Name == "" { + return ErrMissingName + } + + p := "/resources/stores/secret/" + i.ID + "/secrets/" + i.Name + + resp, err := c.Delete(p, &RequestOptions{ + Headers: map[string]string{ + "Content-Type": "application/json", + "Accept": "application/json", + }, + Parallel: true, + }) + if err != nil { + return err + } + return resp.Body.Close() +} diff --git a/fastly/secret_store_test.go b/fastly/secret_store_test.go new file mode 100644 index 000000000..cc69cff32 --- /dev/null +++ b/fastly/secret_store_test.go @@ -0,0 +1,420 @@ +package fastly + +import ( + "bytes" + "fmt" + "sort" + "testing" +) + +func TestClient_CreateSecretStore(t *testing.T) { + t.Parallel() + + var ( + ss *SecretStore + err error + ) + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + ss, err = c.CreateSecretStore(&CreateSecretStoreInput{ + Name: t.Name(), + }) + }) + if err != nil { + t.Fatalf("error creating secret store: %v", err) + } + + // Ensure Secret Store is cleaned up. + t.Cleanup(func() { + record(t, fmt.Sprintf("secret_store/%s/delete_store", t.Name()), func(c *Client) { + err = c.DeleteSecretStore(&DeleteSecretStoreInput{ + ID: ss.ID, + }) + }) + if err != nil { + t.Fatalf("error deleting secret store %q: %v", ss.ID, err) + } + }) + + if got := ss.ID; len(got) == 0 { + t.Errorf("ID: got %q, want not empty", got) + } + if got, want := ss.Name, t.Name(); got != want { + t.Errorf("Name: got %q, want %q", got, want) + } +} + +func TestClient_ListSecretStores(t *testing.T) { + // Cannot be run in parallel, since the list of stores is singular + // for test user. + + var ( + stores []*SecretStore + err error + ) + for i := 0; i < 5; i++ { + ss := createSecretStoreHelper(t, i) + stores = append(stores, ss) + } + sort.Slice(stores, func(i, j int) bool { + return stores[i].ID < stores[j].ID + }) + + var list *SecretStores + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + list, err = c.ListSecretStores(&ListSecretStoresInput{}) + }) + if err != nil { + t.Fatalf("error listing secret store: %v", err) + } + + if got, want := len(list.Data), len(stores); got != want { + t.Fatalf("Data: got length %d, want %d", got, want) + } + sort.Slice(list.Data, func(i, j int) bool { + return list.Data[i].ID < list.Data[j].ID + }) + for i, ss := range list.Data { + if got, want := ss.ID, stores[i].ID; got != want { + t.Errorf("Data[%d].ID: got %q, %q", i, got, want) + } + if got, want := ss.Name, stores[i].Name; got != want { + t.Errorf("Data[%d].Name: got %q, %q", i, got, want) + } + } + + if got, wantMin := list.Meta.Limit, len(stores); got < wantMin { + t.Errorf("Meta.Limit: got %d, want >= %d", got, wantMin) + } + // Only a single page of results is expected. + if got, want := list.Meta.NextCursor, ""; got != want { + t.Errorf("Meta.NextCursor: got %q, want %q", got, want) + } +} + +func TestClient_GetSecretStore(t *testing.T) { + t.Parallel() + + ss := createSecretStoreHelper(t, 0) + + var ( + store *SecretStore + err error + ) + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + store, err = c.GetSecretStore(&GetSecretStoreInput{ + ID: ss.ID, + }) + }) + if err != nil { + t.Fatalf("error getting secret store: %v", err) + } + + if got, want := store.ID, ss.ID; got != want { + t.Errorf("ID: got %q, want %q", got, want) + } + if got, want := store.Name, ss.Name; got != want { + t.Errorf("Name: got %q, want %q", got, want) + } +} + +func TestClient_DeleteSecretStore(t *testing.T) { + t.Parallel() + + var ( + ss *SecretStore + err error + ) + record(t, fmt.Sprintf("secret_store/%s/create_store", t.Name()), func(c *Client) { + ss, err = c.CreateSecretStore(&CreateSecretStoreInput{ + Name: t.Name(), + }) + }) + if err != nil { + t.Fatalf("error creating secret store: %v", err) + } + + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + err = c.DeleteSecretStore(&DeleteSecretStoreInput{ + ID: ss.ID, + }) + }) + if err != nil { + t.Fatalf("error deleting secret store: %v", err) + } +} + +func TestClient_CreateSecret(t *testing.T) { + t.Parallel() + + ss := createSecretStoreHelper(t, 0) + + var ( + s *Secret + err error + ) + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + s, err = c.CreateSecret(&CreateSecretInput{ + ID: ss.ID, + Name: t.Name(), + Secret: []byte("secretum servare"), + }) + }) + if err != nil { + t.Fatalf("error creating secret: %v", err) + } + + if got, want := s.Name, t.Name(); got != want { + t.Errorf("Name: got %q, want %q", got, want) + } + if got := s.Digest; len(got) == 0 { + t.Errorf("Digest: got %q, want not blank", string(got)) + } +} + +func TestClient_ListSecrets(t *testing.T) { + t.Parallel() + + ss := createSecretStoreHelper(t, 0) + + var ( + secrets []*Secret + s *Secret + err error + ) + for i := 0; i < 5; i++ { + record(t, fmt.Sprintf("secret_store/%s/create_secret_%02d", t.Name(), i), func(c *Client) { + s, err = c.CreateSecret(&CreateSecretInput{ + ID: ss.ID, + Name: fmt.Sprintf("%s-%02d", t.Name(), i), + Secret: []byte("secretum servare"), + }) + }) + if err != nil { + t.Fatalf("error creating secret: %v", err) + } + secrets = append(secrets, s) + } + + var list *Secrets + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + list, err = c.ListSecrets(&ListSecretsInput{ + ID: ss.ID, + }) + }) + if err != nil { + t.Fatalf("error listing secrets: %v", err) + } + + if got, want := len(list.Data), len(secrets); got != want { + t.Fatalf("Data: got length %d, want %d", got, want) + } + sort.Slice(list.Data, func(i, j int) bool { + return list.Data[i].Name < list.Data[j].Name + }) + for i, s := range list.Data { + if got, want := s.Name, secrets[i].Name; got != want { + t.Errorf("Data[%d].Name: got %q, %q", i, got, want) + } + if got, want := s.Digest, secrets[i].Digest; !bytes.Equal(got, want) { + t.Errorf("Data[%d].Digest: got %q, %q", i, string(got), string(want)) + } + } + + if got, wantMin := list.Meta.Limit, len(secrets); got < wantMin { + t.Errorf("Meta.Limit: got %d, want >= %d", got, wantMin) + } + // Only a single page of results is expected. + if got, want := list.Meta.NextCursor, ""; got != want { + t.Errorf("Meta.NextCursor: got %q, want %q", got, want) + } +} + +func TestClient_GetSecret(t *testing.T) { + t.Parallel() + + ss := createSecretStoreHelper(t, 0) + + var ( + s *Secret + err error + ) + record(t, fmt.Sprintf("secret_store/%s/create_secret", t.Name()), func(c *Client) { + s, err = c.CreateSecret(&CreateSecretInput{ + ID: ss.ID, + Name: t.Name(), + Secret: []byte("secretum servare"), + }) + }) + if err != nil { + t.Fatalf("error creating secret: %v", err) + } + + var secret *Secret + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + secret, err = c.GetSecret(&GetSecretInput{ + ID: ss.ID, + Name: s.Name, + }) + }) + if err != nil { + t.Fatalf("error getting secret: %v", err) + } + + if got, want := secret.Name, s.Name; got != want { + t.Errorf("Name: got %q, %q", got, want) + } + if got, want := secret.Digest, s.Digest; !bytes.Equal(got, want) { + t.Errorf("Digest: got %q, %q", string(got), string(want)) + } +} + +func TestClient_DeleteSecret(t *testing.T) { + t.Parallel() + + ss := createSecretStoreHelper(t, 0) + + var ( + s *Secret + err error + ) + record(t, fmt.Sprintf("secret_store/%s/create_secret", t.Name()), func(c *Client) { + s, err = c.CreateSecret(&CreateSecretInput{ + ID: ss.ID, + Name: t.Name(), + Secret: []byte("secretum servare"), + }) + }) + if err != nil { + t.Fatalf("error creating secret: %v", err) + } + + record(t, fmt.Sprintf("secret_store/%s", t.Name()), func(c *Client) { + err = c.DeleteSecret(&DeleteSecretInput{ + ID: ss.ID, + Name: s.Name, + }) + }) + if err != nil { + t.Fatalf("error deleting secret: %v", err) + } +} + +func TestClient_SecretStore_validation(t *testing.T) { + t.Parallel() + + var err error + + _, err = testClient.CreateSecretStore(&CreateSecretStoreInput{ + Name: "", + }) + if want := ErrMissingName; err != want { + t.Errorf("CreateSecretStore: got error %v, want %v", err, want) + } + + _, err = testClient.GetSecretStore(&GetSecretStoreInput{ + ID: "", + }) + if want := ErrMissingID; err != want { + t.Errorf("GetSecretStore: got error %v, want %v", err, want) + } + + err = testClient.DeleteSecretStore(&DeleteSecretStoreInput{ + ID: "", + }) + if want := ErrMissingID; err != want { + t.Errorf("DeleteSecretStore: got error %v, want %v", err, want) + } + + _, err = testClient.CreateSecret(&CreateSecretInput{ + ID: "", + Name: "name", + Secret: []byte("secret"), + }) + if want := ErrMissingID; err != want { + t.Errorf("CreateSecret: got error %v, want %v", err, want) + } + _, err = testClient.CreateSecret(&CreateSecretInput{ + ID: "123", + Name: "", + Secret: []byte("secret"), + }) + if want := ErrMissingName; err != want { + t.Errorf("CreateSecret: got error %v, want %v", err, want) + } + _, err = testClient.CreateSecret(&CreateSecretInput{ + ID: "123", + Name: "name", + Secret: []byte(nil), + }) + if want := ErrMissingSecret; err != want { + t.Errorf("CreateSecret: got error %v, want %v", err, want) + } + + _, err = testClient.ListSecrets(&ListSecretsInput{ + ID: "", + }) + if want := ErrMissingID; err != want { + t.Errorf("ListSecrets: got error %v, want %v", err, want) + } + + _, err = testClient.GetSecret(&GetSecretInput{ + ID: "", + Name: "name", + }) + if want := ErrMissingID; err != want { + t.Errorf("GetSecret: got error %v, want %v", err, want) + } + _, err = testClient.GetSecret(&GetSecretInput{ + ID: "id", + Name: "", + }) + if want := ErrMissingName; err != want { + t.Errorf("GetSecret: got error %v, want %v", err, want) + } + + err = testClient.DeleteSecret(&DeleteSecretInput{ + ID: "", + Name: "name", + }) + if want := ErrMissingID; err != want { + t.Errorf("DeleteSecret: got error %v, want %v", err, want) + } + err = testClient.DeleteSecret(&DeleteSecretInput{ + ID: "id", + Name: "", + }) + if want := ErrMissingName; err != want { + t.Errorf("DeleteSecret: got error %v, want %v", err, want) + } +} + +func createSecretStoreHelper(t *testing.T, i int) *SecretStore { + t.Helper() + + var ( + ss *SecretStore + err error + ) + record(t, fmt.Sprintf("secret_store/%s/create_store_%02d", t.Name(), i), func(c *Client) { + ss, err = c.CreateSecretStore(&CreateSecretStoreInput{ + Name: fmt.Sprintf("%s-%02d", t.Name(), i), + }) + }) + if err != nil { + t.Fatalf("error creating secret store: %v", err) + } + + // Cleanup secret store. + t.Cleanup(func() { + record(t, fmt.Sprintf("secret_store/%s/delete_store_%02d", t.Name(), i), func(c *Client) { + err = c.DeleteSecretStore(&DeleteSecretStoreInput{ + ID: ss.ID, + }) + }) + if err != nil { + t.Fatalf("error deleting secret store %q: %v", ss.ID, err) + } + }) + + return ss +}