• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-fuego / fuego / 13054982190

30 Jan 2025 02:59PM UTC coverage: 93.628% (-0.3%) from 93.882%
13054982190

Pull #344

github

dylanhitt
chore: proper capitalization

Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com>
Pull Request #344: WIP: OpenAPIServable

44 of 48 new or added lines in 4 files covered. (91.67%)

4 existing lines in 1 file now uncovered.

2395 of 2558 relevant lines covered (93.63%)

1.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.63
/engine.go
1
package fuego
2

3
import (
4
        "context"
5
        "encoding/json"
6
        "fmt"
7
        "log/slog"
8
        "net/http"
9
        "os"
10
        "path/filepath"
11

12
        "github.com/getkin/kin-openapi/openapi3"
13
)
14

15
// NewEngine creates a new Engine with the given options.
16
// For example:
17
//
18
//        engine := fuego.NewEngin(
19
//                WithOpenAPIConfig(
20
//                        OpenAPIConfig{
21
//                                PrettyFormatJSON: true,
22
//                        },
23
//                ),
24
//        )
25
//
26
// Options all begin with `With`.
27
func NewEngine(options ...func(*Engine)) *Engine {
1✔
28
        e := &Engine{
1✔
29
                OpenAPI:       NewOpenAPI(),
1✔
30
                OpenAPIConfig: defaultOpenAPIConfig,
1✔
31
                ErrorHandler:  ErrorHandler,
1✔
32
        }
1✔
33
        for _, option := range options {
2✔
34
                option(e)
1✔
35
        }
1✔
36
        return e
1✔
37
}
38

39
// The Engine is the main struct of the framework.
40
type Engine struct {
41
        OpenAPI       *OpenAPI
42
        ErrorHandler  func(error) error
43
        OpenAPIConfig OpenAPIConfig
44

45
        requestContentTypes []string
46
}
47

48
type OpenAPIConfig struct {
49
        // Local path to save the OpenAPI JSON spec
50
        JSONFilePath string
51
        // If true, the server will not serve nor generate any OpenAPI resources
52
        Disabled bool
53
        // If true, the engine will not print messages
54
        DisableMessages bool
55
        // If true, the engine will not save the OpenAPI JSON spec locally
56
        DisableLocalSave bool
57
        // Pretty prints the OpenAPI spec with proper JSON indentation
58
        PrettyFormatJSON bool
59
        // URL to serve the OpenAPI JSON spec
60
        SpecURL string
61
        // Handler to serve the OpenAPI UI from spec URL
62
        UIHandler func(specURL string) http.Handler
63
        // URL to serve the swagger UI
64
        SwaggerURL string
65
        // If true, the server will not serve the Swagger UI
66
        DisableSwaggerUI bool
67
}
68

69
var defaultOpenAPIConfig = OpenAPIConfig{
70
        JSONFilePath: "doc/openapi.json",
71
        SpecURL:      "/swagger/openapi.json",
72
        SwaggerURL:   "/swagger",
73
        UIHandler:    DefaultOpenAPIHandler,
74
}
75

76
// WithRequestContentType sets the accepted content types for the engine.
77
// By default, the accepted content types is */*.
78
func WithRequestContentType(consumes ...string) func(*Engine) {
1✔
79
        return func(e *Engine) { e.requestContentTypes = consumes }
2✔
80
}
81

82
func WithOpenAPIConfig(config OpenAPIConfig) func(*Engine) {
1✔
83
        return func(e *Engine) {
2✔
84
                if config.JSONFilePath != "" {
2✔
85
                        e.OpenAPIConfig.JSONFilePath = config.JSONFilePath
1✔
86
                }
1✔
87
                if config.SpecURL != "" {
2✔
88
                        e.OpenAPIConfig.SpecURL = config.SpecURL
1✔
89
                }
1✔
90
                if config.SwaggerURL != "" {
2✔
91
                        e.OpenAPIConfig.SwaggerURL = config.SwaggerURL
1✔
92
                }
1✔
93
                if config.UIHandler != nil {
2✔
94
                        e.OpenAPIConfig.UIHandler = config.UIHandler
1✔
95
                }
1✔
96

97
                e.OpenAPIConfig.Disabled = config.Disabled
1✔
98
                e.OpenAPIConfig.DisableLocalSave = config.DisableLocalSave
1✔
99
                e.OpenAPIConfig.PrettyFormatJSON = config.PrettyFormatJSON
1✔
100
                e.OpenAPIConfig.DisableSwaggerUI = config.DisableSwaggerUI
1✔
101

1✔
102
                if !validateSpecURL(e.OpenAPIConfig.SpecURL) {
2✔
103
                        slog.Error("Error serving OpenAPI JSON spec. Value of 's.OpenAPIServerConfig.SpecURL' option is not valid", "url", e.OpenAPIConfig.SpecURL)
1✔
104
                        return
1✔
105
                }
1✔
106
                if !validateSwaggerURL(e.OpenAPIConfig.SwaggerURL) {
2✔
107
                        slog.Error("Error serving Swagger UI. Value of 's.OpenAPIServerConfig.SwaggerURL' option is not valid", "url", e.OpenAPIConfig.SwaggerURL)
1✔
108
                        return
1✔
109
                }
1✔
110
        }
111
}
112

113
// WithErrorHandler sets a customer error handler for the server
114
func WithErrorHandler(errorHandler func(err error) error) func(*Engine) {
1✔
115
        return func(e *Engine) {
2✔
116
                if errorHandler == nil {
2✔
117
                        panic("errorHandler cannot be nil")
1✔
118
                }
119

120
                e.ErrorHandler = errorHandler
1✔
121
        }
122
}
123

124
// DisableErrorHandler overrides ErrorHandler with a simple pass-through
125
func DisableErrorHandler() func(*Engine) {
1✔
126
        return func(e *Engine) {
2✔
127
                e.ErrorHandler = func(err error) error { return err }
2✔
128
        }
129
}
130

131
func (e *Engine) SpecHandler() func(c ContextNoBody) (openapi3.T, error) {
1✔
132
        return func(c ContextNoBody) (openapi3.T, error) {
1✔
NEW
133
                return *e.OpenAPI.Description(), nil
×
NEW
134
        }
×
135
}
136

137
// OutputOpenAPISpec takes the OpenAPI spec and outputs it to a JSON file
138
func (e *Engine) OutputOpenAPISpec() *openapi3.T {
1✔
139
        e.OpenAPI.computeTags()
1✔
140

1✔
141
        // Validate
1✔
142
        err := e.OpenAPI.Description().Validate(context.Background())
1✔
143
        if err != nil {
1✔
144
                slog.Error("Error validating spec", "error", err)
×
145
        }
×
146

147
        // Marshal spec to JSON
148
        jsonSpec, err := e.marshalSpec()
1✔
149
        if err != nil {
1✔
150
                slog.Error("Error marshaling spec to JSON", "error", err)
×
151
        }
×
152

153
        if !e.OpenAPIConfig.DisableLocalSave {
2✔
154
                err := e.saveOpenAPIToFile(e.OpenAPIConfig.JSONFilePath, jsonSpec)
1✔
155
                if err != nil {
1✔
156
                        slog.Error("Error saving spec to local path", "error", err, "path", e.OpenAPIConfig.JSONFilePath)
×
157
                }
×
158
        }
159
        return e.OpenAPI.Description()
1✔
160
}
161

162
func (e *Engine) saveOpenAPIToFile(jsonSpecLocalPath string, jsonSpec []byte) error {
1✔
163
        jsonFolder := filepath.Dir(jsonSpecLocalPath)
1✔
164

1✔
165
        err := os.MkdirAll(jsonFolder, 0o750)
1✔
166
        if err != nil {
1✔
167
                return fmt.Errorf("error creating docs directory: %w", err)
×
168
        }
×
169

170
        f, err := os.Create(jsonSpecLocalPath) // #nosec G304 (file path provided by developer, not by user)
1✔
171
        if err != nil {
2✔
172
                return fmt.Errorf("error creating file: %w", err)
1✔
173
        }
1✔
174
        defer f.Close()
1✔
175

1✔
176
        _, err = f.Write(jsonSpec)
1✔
177
        if err != nil {
1✔
178
                return fmt.Errorf("error writing file: %w", err)
×
179
        }
×
180

181
        e.printOpenAPIMessage("JSON file: " + jsonSpecLocalPath)
1✔
182
        return nil
1✔
183
}
184

185
func (e *Engine) marshalSpec() ([]byte, error) {
1✔
186
        if e.OpenAPIConfig.PrettyFormatJSON {
2✔
187
                return json.MarshalIndent(e.OpenAPI.Description(), "", "\t")
1✔
188
        }
1✔
189
        return json.Marshal(e.OpenAPI.Description())
1✔
190
}
191

192
func (e *Engine) printOpenAPIMessage(msg string) {
1✔
193
        if !e.OpenAPIConfig.DisableMessages {
2✔
194
                slog.Info(msg)
1✔
195
        }
1✔
196
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc