Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions openapi31/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,41 @@
})
}

// AddWebhook validates and sets webhook by name and method.
//
// It will fail if webhook with same name already exists.
func (s *Spec) AddWebhook(method, name string, operation Operation) error {
if _, found := s.Webhooks[name]; found {
return fmt.Errorf("duplicate webhook name: %s", name)
}

Check notice on line 191 in openapi31/helper.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 189:191 are not covered by tests.

method = strings.ToLower(method)

// Add "No Content" response if there are no responses configured.
if len(operation.ResponsesEns().MapOfResponseOrReferenceValues) == 0 && operation.Responses.Default == nil {
operation.Responses.WithMapOfResponseOrReferenceValuesItem(strconv.Itoa(http.StatusNoContent), ResponseOrReference{
Response: &Response{
Description: http.StatusText(http.StatusNoContent),
},
})
}

Check notice on line 202 in openapi31/helper.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 196:202 are not covered by tests.

method, _, _, err := openapi.SanitizeMethodPath(method, "/")
if err != nil {
return err
}

Check notice on line 207 in openapi31/helper.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 205:207 are not covered by tests.

pathItem := PathItem{}

if err := pathItem.SetOperation(method, &operation); err != nil {
return err
}

Check notice on line 213 in openapi31/helper.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 211:213 are not covered by tests.

s.WithWebhooksItem(name, pathItem.PathItemOrReference())

return nil
}

// UnknownParamIsForbidden indicates forbidden unknown parameters.
func (o Operation) UnknownParamIsForbidden(in ParameterIn) bool {
f, ok := o.MapOfAnything[xForbidUnknown+string(in)].(bool)
Expand Down Expand Up @@ -281,3 +316,10 @@
r.ReferenceEns().Ref = ref
r.RequestBody = nil
}

// PathItemOrReference exposes PathItem as union type.
func (p *PathItem) PathItemOrReference() PathItemOrReference {
return PathItemOrReference{
PathItem: p,
}
}
39 changes: 29 additions & 10 deletions openapi31/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

r.DefaultOptions = append(r.DefaultOptions, jsonschema.InterceptSchema(func(params jsonschema.InterceptSchemaParams) (stop bool, err error) {
// See https://spec.openapis.org/oas/v3.1.0.html#data-types.
switch params.Value.Kind() { //nolint:exhaustive // Not all kinds have formats defined.
switch params.Value.Kind() { //nolint // Not all kinds have formats defined.
case reflect.Int64:
params.Schema.WithFormat("int64")
case reflect.Int32:
Expand All @@ -46,9 +46,9 @@
}

// NewOperationContext initializes openapi.OperationContext to be prepared
// and added later with Reflector.AddOperation.
func (r *Reflector) NewOperationContext(method, pathPattern string) (openapi.OperationContext, error) {
method, pathPattern, pathParams, err := openapi.SanitizeMethodPath(method, pathPattern)
// and added later with Reflector.AddOperation or Reflector.AddWebhook.
func (r *Reflector) NewOperationContext(method, pathPatternOrWebhookName string) (openapi.OperationContext, error) {
method, pathPattern, pathParams, err := openapi.SanitizeMethodPath(method, pathPatternOrWebhookName)
if err != nil {
return nil, err
}
Expand All @@ -61,7 +61,7 @@
}

if operation != nil {
return nil, fmt.Errorf("operation already exists: %s %s", method, pathPattern)
return nil, fmt.Errorf("operation already exists: %s %s", method, pathPatternOrWebhookName)

Check notice on line 64 in openapi31/reflect.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 63:65 are not covered by tests.
}

operation = &Operation{}
Expand Down Expand Up @@ -211,24 +211,43 @@

// AddOperation configures operation request and response schema.
func (r *Reflector) AddOperation(oc openapi.OperationContext) error {
c, err := r.setupOC(oc)
if err != nil {
return err
}

Check notice on line 217 in openapi31/reflect.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 215:217 are not covered by tests.

return r.SpecEns().AddOperation(oc.Method(), oc.PathPattern(), *c.op)
}

// AddWebhook configures webhook request and response schema.
func (r *Reflector) AddWebhook(oc openapi.OperationContext) error {
c, err := r.setupOC(oc)
if err != nil {
return err
}

Check notice on line 227 in openapi31/reflect.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 225:227 are not covered by tests.

return r.SpecEns().AddWebhook(oc.Method(), c.PathPattern(), *c.op)
}

func (r *Reflector) setupOC(oc openapi.OperationContext) (operationContext, error) {
c, ok := oc.(operationContext)
if !ok {
return fmt.Errorf("wrong operation context %T received, %T expected", oc, operationContext{})
return c, fmt.Errorf("wrong operation context %T received, %T expected", oc, operationContext{})

Check notice on line 235 in openapi31/reflect.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 234:236 are not covered by tests.
}

if err := r.setupRequest(c.op, oc); err != nil {
return fmt.Errorf("setup request %s %s: %w", oc.Method(), oc.PathPattern(), err)
return c, fmt.Errorf("setup request %s %s: %w", oc.Method(), oc.PathPattern(), err)

Check notice on line 239 in openapi31/reflect.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 238:240 are not covered by tests.
}

if err := c.op.validatePathParams(c.pathParams); err != nil {
return fmt.Errorf("validate path params %s %s: %w", oc.Method(), oc.PathPattern(), err)
return c, fmt.Errorf("validate path params %s %s: %w", oc.Method(), oc.PathPattern(), err)

Check notice on line 243 in openapi31/reflect.go

View workflow job for this annotation

GitHub Actions / test (stable)

1 statement(s) on lines 242:244 are not covered by tests.
}

if err := r.setupResponse(c.op, oc); err != nil {
return fmt.Errorf("setup response %s %s: %w", oc.Method(), oc.PathPattern(), err)
return c, fmt.Errorf("setup response %s %s: %w", oc.Method(), oc.PathPattern(), err)
}

return r.SpecEns().AddOperation(oc.Method(), oc.PathPattern(), *c.op)
return c, nil
}

func (r *Reflector) setupRequest(o *Operation, oc openapi.OperationContext) error {
Expand Down
52 changes: 52 additions & 0 deletions openapi31/reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1594,3 +1594,55 @@ func TestSelfReference(t *testing.T) {
}
}`, reflector.SpecSchema())
}

func TestReflector_AddWebhook(t *testing.T) {
r := openapi31.NewReflector()

oc, err := r.NewOperationContext(http.MethodPost, "newPet")
require.NoError(t, err)

type Pet struct {
Name string `json:"name"`
Breed string `json:"breed"`
}

oc.AddReqStructure(Pet{}, func(cu *openapi.ContentUnit) {
cu.Description = "Information about a new pet in the system"
})

oc.AddRespStructure(nil, func(cu *openapi.ContentUnit) {
cu.Description = "Return a 200 status to indicate that the data was received successfully"
cu.HTTPStatus = http.StatusOK
})

require.NoError(t, r.AddWebhook(oc))

assertjson.EqMarshal(t, `{
"openapi":"3.1.0","info":{"title":"","version":""},"paths":{},
"webhooks":{
"newPet":{
"post":{
"requestBody":{
"description":"Information about a new pet in the system",
"content":{
"application/json":{"schema":{"$ref":"#/components/schemas/Openapi31TestPet"}}
}
},
"responses":{
"200":{
"description":"Return a 200 status to indicate that the data was received successfully"
}
}
}
}
},
"components":{
"schemas":{
"Openapi31TestPet":{
"properties":{"breed":{"type":"string"},"name":{"type":"string"}},
"type":"object"
}
}
}
}`, r.SpecEns())
}
Loading