-
Notifications
You must be signed in to change notification settings - Fork 539
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
APISHI-2357 Add support for Get/Post/Delete/Patch schemas for API Shi…
…eld Schema Validation 2.0 This change adds support for the following API Shield related endpoints related to Schema Validation 2.0: - Retrieve information about all schemas on a zone - Retrieve information about a specific schema on a zone - Upload a schema to a zone - Enable validation for a schema - Delete a schema
- Loading branch information
Showing
3 changed files
with
821 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:enhancement | ||
Add support for Get/Post/Delete/Patch schemas for API Shield Schema Validation 2.0 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"io" | ||
"mime/multipart" | ||
"net/http" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/goccy/go-json" | ||
) | ||
|
||
// APIShieldSchema represents a schema stored in API Shield Schema Validation 2.0. | ||
type APIShieldSchema struct { | ||
// ID represents the ID of the schema | ||
ID string `json:"schema_id"` | ||
// Name represents the name of the schema | ||
Name string `json:"name"` | ||
// Kind of the schema | ||
Kind string `json:"kind"` | ||
// Source is the contents of the schema | ||
Source string `json:"source,omitempty"` | ||
// CreatedAt is the time the schema was created | ||
CreatedAt time.Time `json:"created_at,omitempty"` | ||
// ValidationEnabled controls if schema is used for validation | ||
ValidationEnabled bool `json:"validation_enabled,omitempty"` | ||
} | ||
|
||
// CreateAPIShieldSchemaParams represents the parameters to pass when creating a schema in Schema Validation 2.0. | ||
// | ||
// API documentation: TODO. | ||
type CreateAPIShieldSchemaParams struct { | ||
// Source is a io.Reader containing the contents of the schema | ||
Source io.Reader | ||
// Name represents the name of the schema. | ||
Name string | ||
// Kind of the schema. This is always set to openapi_v3. | ||
Kind string | ||
// ValidationEnabled controls if schema is used for validation | ||
ValidationEnabled *bool | ||
} | ||
|
||
// GetAPIShieldSchemaParams represents the parameters to pass when retrieving a schema with a given schema ID. | ||
// | ||
// API documentation: TODO. | ||
type GetAPIShieldSchemaParams struct { | ||
// SchemaID is the ID of the schema to retrieve | ||
SchemaID string `url:"-"` | ||
|
||
// OmitSource specifies whether the contents of the schema should be returned in the "Source" field. | ||
OmitSource *bool `url:"omit_source,omitempty"` | ||
} | ||
|
||
// ListAPIShieldSchemasParams represents the parameters to pass when retrieving all schemas. | ||
// | ||
// API documentation: TODO. | ||
type ListAPIShieldSchemasParams struct { | ||
// OmitSource specifies whether the contents of the schema should be returned in the "Source" field. | ||
OmitSource *bool `url:"omit_source,omitempty"` | ||
|
||
// ValidationEnabled specifies whether to return only schemas that have validation enabled. | ||
ValidationEnabled *bool `url:"validation_enabled,omitempty"` | ||
|
||
// PaginationOptions to apply to the request. | ||
PaginationOptions | ||
} | ||
|
||
// DeleteAPIShieldSchemaParams represents the parameters to pass to delete a schema. | ||
// | ||
// API documentation: TODO. | ||
type DeleteAPIShieldSchemaParams struct { | ||
// SchemaID is the schema to be deleted | ||
SchemaID string `url:"-"` | ||
} | ||
|
||
// PatchAPIShieldSchemaParams represents the parameters to pass to patch certain fields on an existing schema | ||
// | ||
// API documentation: TODO. | ||
type PatchAPIShieldSchemaParams struct { | ||
// SchemaID is the schema to be patched | ||
SchemaID string `json:"-" url:"-"` | ||
|
||
// ValidationEnabled controls if schema is used for validation | ||
ValidationEnabled *bool `json:"validation_enabled" url:"-"` | ||
} | ||
|
||
// APIShieldGetSchemaResponse represents the response from the GET api_gateway/user_schemas/{id} endpoint. | ||
type APIShieldGetSchemaResponse struct { | ||
Result APIShieldSchema `json:"result"` | ||
Response | ||
} | ||
|
||
// APIShieldListSchemasResponse represents the response from the GET api_gateway/user_schemas endpoint. | ||
type APIShieldListSchemasResponse struct { | ||
Result []APIShieldSchema `json:"result"` | ||
ResultInfo `json:"result_info"` | ||
Response | ||
} | ||
|
||
// APIShieldCreateSchemaResponse represents the response from the POST api_gateway/user_schemas endpoint. | ||
type APIShieldCreateSchemaResponse struct { | ||
Result APIShieldCreateSchemaResult `json:"result"` | ||
Response | ||
} | ||
|
||
// APIShieldDeleteSchemaResponse represents the response from the DELETE api_gateway/user_schemas/{id} endpoint. | ||
type APIShieldDeleteSchemaResponse struct { | ||
Result interface{} `json:"result"` | ||
Response | ||
} | ||
|
||
// APIShieldPatchSchemaResponse represents the response from the PATCH api_gateway/user_schemas/{id} endpoint. | ||
type APIShieldPatchSchemaResponse struct { | ||
Result APIShieldSchema `json:"result"` | ||
Response | ||
} | ||
|
||
// APIShieldCreateSchemaResult represents the successful result of creating a schema in Schema Validation 2.0. | ||
type APIShieldCreateSchemaResult struct { | ||
// APIShieldSchema is the schema that was created | ||
Schema APIShieldSchema `json:"schema"` | ||
// APIShieldCreateSchemaEvents are non-critical event logs that occurred during processing. | ||
Events APIShieldCreateSchemaEvents `json:"upload_details"` | ||
} | ||
|
||
// APIShieldCreateSchemaEvents are event logs that occurred during processing. | ||
// | ||
// The logs are separated into levels of severity. | ||
type APIShieldCreateSchemaEvents struct { | ||
Critical *APIShieldCreateSchemaEventWithLocation `json:"critical,omitempty"` | ||
Errors []APIShieldCreateSchemaEventWithLocations `json:"errors,omitempty"` | ||
Warnings []APIShieldCreateSchemaEventWithLocations `json:"warnings,omitempty"` | ||
} | ||
|
||
// APIShieldCreateSchemaEvent is an event log that occurred during processing. | ||
type APIShieldCreateSchemaEvent struct { | ||
// Code identifies the event that occurred | ||
Code uint `json:"code"` | ||
// Message describes the event that occurred | ||
Message string `json:"message"` | ||
} | ||
|
||
// APIShieldCreateSchemaEventWithLocation is an event log that occurred during processing, with the location | ||
// in the schema where the event occurred. | ||
type APIShieldCreateSchemaEventWithLocation struct { | ||
APIShieldCreateSchemaEvent | ||
|
||
// Location is where the event occurred | ||
// See https://goessner.net/articles/JsonPath/ for JSONPath specification. | ||
Location string `json:"location,omitempty"` | ||
} | ||
|
||
// APIShieldCreateSchemaEventWithLocations is an event log that occurred during processing, with the location(s) | ||
// in the schema where the event occurred. | ||
type APIShieldCreateSchemaEventWithLocations struct { | ||
APIShieldCreateSchemaEvent | ||
|
||
// Locations lists JSONPath locations where the event occurred | ||
// See https://goessner.net/articles/JsonPath/ for JSONPath specification | ||
Locations []string `json:"locations"` | ||
} | ||
|
||
// GetAPIShieldSchema retrieves information about a specific schema on a zone | ||
// | ||
// API documentation: TODO. | ||
func (api *API) GetAPIShieldSchema(ctx context.Context, rc *ResourceContainer, params GetAPIShieldSchemaParams) (*APIShieldSchema, error) { | ||
if params.SchemaID == "" { | ||
return nil, fmt.Errorf("params.schemaID must be provided") | ||
} | ||
|
||
path := fmt.Sprintf("/zones/%s/api_gateway/user_schemas/%s", rc.Identifier, params.SchemaID) | ||
|
||
uri := buildURI(path, params) | ||
|
||
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var asResponse APIShieldGetSchemaResponse | ||
err = json.Unmarshal(res, &asResponse) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) | ||
} | ||
|
||
return &asResponse.Result, nil | ||
} | ||
|
||
// ListAPIShieldSchemas retrieves all schemas for a zone | ||
// | ||
// API documentation: TODO. | ||
func (api *API) ListAPIShieldSchemas(ctx context.Context, rc *ResourceContainer, params ListAPIShieldSchemasParams) ([]APIShieldSchema, ResultInfo, error) { | ||
path := fmt.Sprintf("/zones/%s/api_gateway/user_schemas", rc.Identifier) | ||
|
||
uri := buildURI(path, params) | ||
|
||
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) | ||
if err != nil { | ||
return nil, ResultInfo{}, err | ||
} | ||
|
||
var asResponse APIShieldListSchemasResponse | ||
err = json.Unmarshal(res, &asResponse) | ||
if err != nil { | ||
return nil, ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err) | ||
} | ||
|
||
return asResponse.Result, asResponse.ResultInfo, nil | ||
} | ||
|
||
// CreateAPIShieldSchema uploads a schema to a zone | ||
// | ||
// API documentation: TODO. | ||
func (api *API) CreateAPIShieldSchema(ctx context.Context, rc *ResourceContainer, params CreateAPIShieldSchemaParams) (*APIShieldCreateSchemaResult, error) { | ||
uri := fmt.Sprintf("/zones/%s/api_gateway/user_schemas", rc.Identifier) | ||
|
||
if params.Name == "" { | ||
return nil, fmt.Errorf("params.Name must not be empty") | ||
} | ||
|
||
if params.Source == nil { | ||
return nil, fmt.Errorf("params.Source must not be nil") | ||
} | ||
|
||
// Prepare the form to be submitted | ||
var b bytes.Buffer | ||
w := multipart.NewWriter(&b) | ||
// write fields | ||
if err := w.WriteField("name", params.Name); err != nil { | ||
return nil, fmt.Errorf("error during multi-part form construction: %w", err) | ||
} | ||
if err := w.WriteField("kind", params.Kind); err != nil { | ||
return nil, fmt.Errorf("error during multi-part form construction: %w", err) | ||
} | ||
|
||
if params.ValidationEnabled != nil { | ||
if err := w.WriteField("validation_enabled", strconv.FormatBool(*params.ValidationEnabled)); err != nil { | ||
return nil, fmt.Errorf("error during multi-part form construction: %w", err) | ||
} | ||
} | ||
|
||
// write schema contents | ||
part, err := w.CreateFormFile("file", params.Name) | ||
if err != nil { | ||
return nil, fmt.Errorf("error during multi-part form construction: %w", err) | ||
} | ||
if _, err := io.Copy(part, params.Source); err != nil { | ||
return nil, fmt.Errorf("error during multi-part form construction: %w", err) | ||
} | ||
if err := w.Close(); err != nil { | ||
return nil, fmt.Errorf("error during multi-part form construction: %w", err) | ||
} | ||
|
||
res, err := api.makeRequestContextWithHeaders(ctx, http.MethodPost, uri, &b, http.Header{ | ||
"Content-Type": []string{w.FormDataContentType()}, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var asResponse APIShieldCreateSchemaResponse | ||
err = json.Unmarshal(res, &asResponse) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) | ||
} | ||
|
||
return &asResponse.Result, nil | ||
} | ||
|
||
// DeleteAPIShieldSchema deletes a single schema | ||
// | ||
// API documentation: TODO. | ||
func (api *API) DeleteAPIShieldSchema(ctx context.Context, rc *ResourceContainer, params DeleteAPIShieldSchemaParams) error { | ||
if params.SchemaID == "" { | ||
return fmt.Errorf("params.schemaID must be provided") | ||
} | ||
|
||
uri := fmt.Sprintf("/zones/%s/api_gateway/user_schemas/%s", rc.Identifier, params.SchemaID) | ||
|
||
res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var asResponse APIShieldDeleteSchemaResponse | ||
err = json.Unmarshal(res, &asResponse) | ||
if err != nil { | ||
return fmt.Errorf("%s: %w", errUnmarshalError, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (api *API) PatchAPIShieldSchema(ctx context.Context, rc *ResourceContainer, params PatchAPIShieldSchemaParams) (*APIShieldSchema, error) { | ||
if params.SchemaID == "" { | ||
return nil, fmt.Errorf("params.schemaID must be provided") | ||
} | ||
|
||
uri := fmt.Sprintf("/zones/%s/api_gateway/user_schemas/%s", rc.Identifier, params.SchemaID) | ||
|
||
res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, params) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Result should be the updated schema that was patched | ||
var asResponse APIShieldPatchSchemaResponse | ||
err = json.Unmarshal(res, &asResponse) | ||
if err != nil { | ||
return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) | ||
} | ||
|
||
return &asResponse.Result, nil | ||
} |
Oops, something went wrong.