Thanks to visit codestin.com
Credit goes to github.com

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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
#internal/dist/openapi.yaml
testing_out.yaml
./testing_out.yaml
internal/dist/openapi.yaml
internal/dist/openapi.yaml
.vscode
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ other parts of the system for that matter.
To run examples, and checkout hosted documentation via Swagger UI, issue the following command:

```sh
$ go run ./examples/*.go
$ go run ./examples/file_output/*.go
# or uncomment line 40 and comment line 38 in internal/dist/index.html before running:
$ go run ./examples/stream_output/*.go
```

And navigate to `http://localhost:3005/docs/api/` in case that you didn't change anything before running the example
Expand Down
20 changes: 18 additions & 2 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func getPathFromFirstElement(cbs []ConfigBuilder) string {
func (o *OAS) BuildDocs(conf ...ConfigBuilder) error {
o.initCallStackForRoutes()

yml, err := marshalToYAML(o)
yml, err := o.marshalToYAML()
if err != nil {
return fmt.Errorf("marshaling issue occurred: %w", err)
}
Expand All @@ -51,7 +51,23 @@ func (o *OAS) BuildDocs(conf ...ConfigBuilder) error {
return nil
}

func marshalToYAML(oas *OAS) ([]byte, error) {
// BuildStream marshals the OAS struct to YAML and writes it to a stream.
//
// Returns an error if there is any.
func (o *OAS) BuildStream(w io.Writer) error {
yml, err := o.marshalToYAML()
if err != nil {
return fmt.Errorf("marshaling issue occurred: %w", err)
}

err = writeAndFlush(yml, w)
if err != nil {
return fmt.Errorf("writing issue occurred: %w", err)
}
return nil
}

func (oas *OAS) marshalToYAML() ([]byte, error) {
transformedOAS := oas.transformToHybridOAS()

yml, err := yaml.Marshal(transformedOAS)
Expand Down
126 changes: 125 additions & 1 deletion build_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package docs

import "testing"
import (
"bytes"
"testing"
)

func TestUnitBuild(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -109,3 +112,124 @@ func TestUnitGetPathFromFirstElem(t *testing.T) {
}

// QUICK CHECK TESTS ARE COMING WITH NEXT RELEASE.

func TestOAS_BuildStream(t *testing.T) {
tests := []struct {
name string
oas *OAS
wantW string
wantErr bool
}{
{
name: "success",
oas: &OAS{
OASVersion: "3.0.1",
Info: Info{
Title: "Test",
Description: "Test object",
},
Components: Components{
Component{
Schemas: Schemas{Schema{
Name: "schema_testing",
Properties: SchemaProperties{
SchemaProperty{
Name: "EnumProp",
Type: "enum",
Description: "short desc",
Enum: []string{"enum", "test", "strSlc"},
},
SchemaProperty{
Name: "intProp",
Type: "integer",
Format: "int64",
Description: "short desc",
Default: 1337,
},
},
XML: XMLEntry{Name: "XML entry test"},
}},
SecuritySchemes: SecuritySchemes{SecurityScheme{
Name: "ses_scheme_testing",
In: "not empty",
Flows: SecurityFlows{SecurityFlow{
Type: "implicit",
AuthURL: "http://petstore.swagger.io/oauth/dialog",
Scopes: SecurityScopes{
SecurityScope{
Name: "write:pets",
Description: "Write to Pets",
},
SecurityScope{
Name: "read:pets",
Description: "Read Pets",
},
},
}},
}},
},
},
},
wantErr: false,
wantW: `openapi: 3.0.1
info:
title: Test
description: Test object
termsOfService: ""
contact:
email: ""
license:
name: ""
url: ""
version: ""
externalDocs:
description: ""
url: ""
servers: []
tags: []
paths: {}
components:
schemas:
schema_testing:
$ref: ""
properties:
EnumProp:
description: short desc
enum:
- enum
- test
- strSlc
type: enum
intProp:
default: 1337
description: short desc
format: int64
type: integer
type: ""
xml:
name: XML entry test
securitySchemes:
ses_scheme_testing:
flows:
implicit:
authorizationUrl: http://petstore.swagger.io/oauth/dialog
scopes:
read:pets: Read Pets
write:pets: Write to Pets
in: not empty
`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &bytes.Buffer{}
if err := tt.oas.BuildStream(w); (err != nil) != tt.wantErr {
t.Errorf("OAS.BuildStream() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotW := w.String(); gotW != tt.wantW {
t.Errorf("OAS.BuildStream() = [%v], want {%v}", gotW, tt.wantW)
}
})
}
}
File renamed without changes.
File renamed without changes.
47 changes: 47 additions & 0 deletions examples/stream_output/logging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"log"
"net/http"
"time"
)

type responseData struct {
status int
size int
}

// our http.ResponseWriter implementation
type loggingResponseWriter struct {
http.ResponseWriter // compose original http.ResponseWriter
responseData *responseData
}

func (r *loggingResponseWriter) Write(b []byte) (int, error) {
size, err := r.ResponseWriter.Write(b) // write response using original http.ResponseWriter
r.responseData.size += size // capture size
return size, err
}

func (r *loggingResponseWriter) WriteHeader(statusCode int) {
r.ResponseWriter.WriteHeader(statusCode) // write status code using original http.ResponseWriter
r.responseData.status = statusCode // capture status code
}

func LogginMiddleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
var (
start = time.Now()
responseData = &responseData{}
)

h.ServeHTTP(&loggingResponseWriter{
ResponseWriter: rw,
responseData: responseData,
}, req)

duration := time.Since(start)

log.Printf("%s[%v] uri:%s duration:%v size:%d", req.Method, responseData.status, req.RequestURI, duration, responseData.size)
})
}
88 changes: 88 additions & 0 deletions examples/stream_output/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"errors"
"fmt"
"net/http"
"os"

"github.com/go-oas/docs"
)

const (
staticRoute = "/docs/api/"
streamRoute = "/docs/oas/"
staticDirectory = "./internal/dist"
port = 3005
)

func main() {
apiDoc := docs.New()
apiSetInfo(&apiDoc)
apiSetTags(&apiDoc)
apiSetServers(&apiDoc)
apiSetExternalDocs(&apiDoc)
apiSetComponents(&apiDoc)

apiDoc.AddRoute(docs.Path{
Route: "/users",
HTTPMethod: "POST",
OperationID: "createUser",
Summary: "Create a new User",
Responses: docs.Responses{
getResponseOK(),
getResponseNotFound(),
},
// HandlerFuncName: "handleCreateUser",
RequestBody: docs.RequestBody{
Description: "Create a new User",
Content: docs.ContentTypes{
getContentApplicationJSON("#/components/schemas/User"),
},
Required: true,
},
})

apiDoc.AddRoute(docs.Path{
Route: "/users",
HTTPMethod: "GET",
OperationID: "getUser",
Summary: "Get a User",
Responses: docs.Responses{
getResponseOK(),
},
// HandlerFuncName: "handleCreateUser",
RequestBody: docs.RequestBody{
Description: "Get a user",
Content: docs.ContentTypes{
getContentApplicationJSON("#/components/schemas/User"),
},
Required: true,
},
})

mux := http.NewServeMux()

// serve static files
fs := http.FileServer(http.Dir(staticDirectory))
mux.Handle(staticRoute, http.StripPrefix(staticRoute, fs))

// serve the oas document from a stream
mux.HandleFunc(streamRoute, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/yaml")
if err := apiDoc.BuildStream(w); err != nil {
http.Error(w, "could not write body", http.StatusInternalServerError)
return
}
})

fmt.Printf("Listening at :%d", port)
if err := http.ListenAndServe(fmt.Sprintf(":%d", port), LogginMiddleware(mux)); err != nil {
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error starting server: %s\n", err)
os.Exit(1)
}
}
}
Loading