-
-
Notifications
You must be signed in to change notification settings - Fork 68
/
Copy pathengine.go
157 lines (134 loc) · 3.89 KB
/
engine.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package fuego
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"os"
"path/filepath"
)
// NewEngine creates a new Engine with the given options.
// For example:
//
// engine := fuego.NewEngin(
// WithOpenAPIConfig(
// OpenAPIConfig{
// PrettyFormatJSON: true,
// },
// ),
// )
//
// Options all begin with `With`.
func NewEngine(options ...func(*Engine)) *Engine {
e := &Engine{
OpenAPI: NewOpenAPI(),
OpenAPIConfig: defaultOpenAPIConfig,
ErrorHandler: ErrorHandler,
}
for _, option := range options {
option(e)
}
return e
}
// The Engine is the main struct of the framework.
type Engine struct {
OpenAPI *OpenAPI
ErrorHandler func(error) error
OpenAPIConfig OpenAPIConfig
requestContentTypes []string
}
type OpenAPIConfig struct {
// Local path to save the OpenAPI JSON spec
JSONFilePath string
// If true, the server will not serve nor generate any OpenAPI resources
Disabled bool
// If true, the engine will not print messages
DisableMessages bool
// If true, the engine will not save the OpenAPI JSON spec locally
DisableLocalSave bool
// Pretty prints the OpenAPI spec with proper JSON indentation
PrettyFormatJSON bool
}
var defaultOpenAPIConfig = OpenAPIConfig{
JSONFilePath: "doc/openapi.json",
}
// WithRequestContentType sets the accepted content types for the engine.
// By default, the accepted content types is */*.
func WithRequestContentType(consumes ...string) func(*Engine) {
return func(e *Engine) { e.requestContentTypes = consumes }
}
func WithOpenAPIConfig(config OpenAPIConfig) func(*Engine) {
return func(e *Engine) {
if config.JSONFilePath != "" {
e.OpenAPIConfig.JSONFilePath = config.JSONFilePath
}
e.OpenAPIConfig.Disabled = config.Disabled
e.OpenAPIConfig.DisableLocalSave = config.DisableLocalSave
e.OpenAPIConfig.PrettyFormatJSON = config.PrettyFormatJSON
}
}
// WithErrorHandler sets a customer error handler for the server
func WithErrorHandler(errorHandler func(err error) error) func(*Engine) {
return func(e *Engine) {
if errorHandler == nil {
panic("errorHandler cannot be nil")
}
e.ErrorHandler = errorHandler
}
}
// DisableErrorHandler overrides ErrorHandler with a simple pass-through
func DisableErrorHandler() func(*Engine) {
return func(e *Engine) {
e.ErrorHandler = func(err error) error { return err }
}
}
// OutputOpenAPISpec takes the OpenAPI spec and outputs it to a JSON file
func (e *Engine) OutputOpenAPISpec() []byte {
e.OpenAPI.computeTags()
// Validate
err := e.OpenAPI.Description().Validate(context.Background())
if err != nil {
slog.Error("Error validating spec", "error", err)
}
// Marshal spec to JSON
jsonSpec, err := e.marshalSpec()
if err != nil {
slog.Error("Error marshaling spec to JSON", "error", err)
}
if !e.OpenAPIConfig.DisableLocalSave {
err := e.saveOpenAPIToFile(e.OpenAPIConfig.JSONFilePath, jsonSpec)
if err != nil {
slog.Error("Error saving spec to local path", "error", err, "path", e.OpenAPIConfig.JSONFilePath)
}
}
return jsonSpec
}
func (e *Engine) saveOpenAPIToFile(jsonSpecLocalPath string, jsonSpec []byte) error {
jsonFolder := filepath.Dir(jsonSpecLocalPath)
err := os.MkdirAll(jsonFolder, 0o750)
if err != nil {
return fmt.Errorf("error creating docs directory: %w", err)
}
f, err := os.Create(jsonSpecLocalPath) // #nosec G304 (file path provided by developer, not by user)
if err != nil {
return fmt.Errorf("error creating file: %w", err)
}
defer f.Close()
_, err = f.Write(jsonSpec)
if err != nil {
return fmt.Errorf("error writing file: %w", err)
}
e.printOpenAPIMessage("JSON file: " + jsonSpecLocalPath)
return nil
}
func (e *Engine) marshalSpec() ([]byte, error) {
if e.OpenAPIConfig.PrettyFormatJSON {
return json.MarshalIndent(e.OpenAPI.Description(), "", "\t")
}
return json.Marshal(e.OpenAPI.Description())
}
func (e *Engine) printOpenAPIMessage(msg string) {
if !e.OpenAPIConfig.DisableMessages {
slog.Info(msg)
}
}