|
1 | 1 | package mockstainless |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "encoding/json" |
5 | 6 | "fmt" |
| 7 | + "io" |
6 | 8 | "net/http" |
7 | 9 | "time" |
| 10 | + |
| 11 | + "github.com/tidwall/gjson" |
8 | 12 | ) |
9 | 13 |
|
10 | 14 | func writeJSON(w http.ResponseWriter, status int, v any) { |
@@ -39,8 +43,8 @@ func newServeMux(m *Mock) http.Handler { |
39 | 43 | "user_code": "DEMO-CODE", |
40 | 44 | "verification_uri": "https://app.stainless.com/activate", |
41 | 45 | "verification_uri_complete": "https://app.stainless.com/activate?code=DEMO-CODE", |
42 | | - "expires_in": 300, |
43 | | - "interval": 1, |
| 46 | + "expires_in": 300, |
| 47 | + "interval": 1, |
44 | 48 | }) |
45 | 49 | }) |
46 | 50 |
|
@@ -85,11 +89,132 @@ func newServeMux(m *Mock) http.Handler { |
85 | 89 | } |
86 | 90 | }) |
87 | 91 |
|
| 92 | + mux.HandleFunc("PATCH /v0/projects/{project}", func(w http.ResponseWriter, r *http.Request) { |
| 93 | + project := r.PathValue("project") |
| 94 | + if project == "" { |
| 95 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 96 | + return |
| 97 | + } |
| 98 | + body := mustReadBody(r) |
| 99 | + writeJSON(w, http.StatusOK, M{ |
| 100 | + "slug": project, |
| 101 | + "display_name": gjson.GetBytes(body, "display_name").String(), |
| 102 | + "object": "project", |
| 103 | + }) |
| 104 | + }) |
| 105 | + |
| 106 | + mux.HandleFunc("POST /v0/projects/{project}/generate_commit_message", func(w http.ResponseWriter, r *http.Request) { |
| 107 | + if r.PathValue("project") == "" { |
| 108 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 109 | + return |
| 110 | + } |
| 111 | + writeJSON(w, http.StatusOK, M{ |
| 112 | + "message": "mock commit message", |
| 113 | + }) |
| 114 | + }) |
| 115 | + |
88 | 116 | mux.HandleFunc("GET /v0/projects/{project}/configs", func(w http.ResponseWriter, r *http.Request) { |
89 | 117 | writeJSON(w, http.StatusOK, m.ProjectConfigs) |
90 | 118 | }) |
91 | 119 |
|
| 120 | + mux.HandleFunc("POST /v0/projects/{project}/configs/guess", func(w http.ResponseWriter, r *http.Request) { |
| 121 | + if r.PathValue("project") == "" { |
| 122 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 123 | + return |
| 124 | + } |
| 125 | + writeJSON(w, http.StatusOK, M{ |
| 126 | + "stainless.yml": M{ |
| 127 | + "content": "# guessed", |
| 128 | + }, |
| 129 | + }) |
| 130 | + }) |
| 131 | + |
| 132 | + mux.HandleFunc("POST /v0/projects/{project}/branches", func(w http.ResponseWriter, r *http.Request) { |
| 133 | + if r.PathValue("project") == "" { |
| 134 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 135 | + return |
| 136 | + } |
| 137 | + body := mustReadBody(r) |
| 138 | + writeJSON(w, http.StatusOK, M{ |
| 139 | + "branch": gjson.GetBytes(body, "branch").String(), |
| 140 | + "object": "project_branch", |
| 141 | + }) |
| 142 | + }) |
| 143 | + |
| 144 | + mux.HandleFunc("GET /v0/projects/{project}/branches", func(w http.ResponseWriter, r *http.Request) { |
| 145 | + if r.PathValue("project") == "" { |
| 146 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 147 | + return |
| 148 | + } |
| 149 | + writeJSON(w, http.StatusOK, Page([]M{ |
| 150 | + {"branch": "main", "object": "project_branch"}, |
| 151 | + })) |
| 152 | + }) |
| 153 | + |
| 154 | + mux.HandleFunc("GET /v0/projects/{project}/branches/{branch}", func(w http.ResponseWriter, r *http.Request) { |
| 155 | + if r.PathValue("project") == "" { |
| 156 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 157 | + return |
| 158 | + } |
| 159 | + writeJSON(w, http.StatusOK, M{ |
| 160 | + "branch": r.PathValue("branch"), |
| 161 | + "object": "project_branch", |
| 162 | + }) |
| 163 | + }) |
| 164 | + |
| 165 | + mux.HandleFunc("DELETE /v0/projects/{project}/branches/{branch}", func(w http.ResponseWriter, r *http.Request) { |
| 166 | + if r.PathValue("project") == "" { |
| 167 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 168 | + return |
| 169 | + } |
| 170 | + writeJSON(w, http.StatusOK, M{"deleted": true}) |
| 171 | + }) |
| 172 | + |
| 173 | + mux.HandleFunc("PUT /v0/projects/{project}/branches/{branch}/rebase", func(w http.ResponseWriter, r *http.Request) { |
| 174 | + if r.PathValue("project") == "" { |
| 175 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 176 | + return |
| 177 | + } |
| 178 | + writeJSON(w, http.StatusOK, M{ |
| 179 | + "branch": r.PathValue("branch"), |
| 180 | + "object": "project_branch", |
| 181 | + }) |
| 182 | + }) |
| 183 | + |
| 184 | + mux.HandleFunc("PUT /v0/projects/{project}/branches/{branch}/reset", func(w http.ResponseWriter, r *http.Request) { |
| 185 | + if r.PathValue("project") == "" { |
| 186 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 187 | + return |
| 188 | + } |
| 189 | + writeJSON(w, http.StatusOK, M{ |
| 190 | + "branch": r.PathValue("branch"), |
| 191 | + "object": "project_branch", |
| 192 | + }) |
| 193 | + }) |
| 194 | + |
| 195 | + mux.HandleFunc("POST /v0/builds", func(w http.ResponseWriter, r *http.Request) { |
| 196 | + body := mustReadBody(r) |
| 197 | + if gjson.GetBytes(body, "project").String() == "" { |
| 198 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 199 | + return |
| 200 | + } |
| 201 | + if len(m.Builds) == 0 { |
| 202 | + writeJSON(w, http.StatusNotFound, M{"error": "missing build"}) |
| 203 | + return |
| 204 | + } |
| 205 | + build := m.CreateBuildFromTemplate(m.Builds[0]) |
| 206 | + if build == nil { |
| 207 | + writeJSON(w, http.StatusNotFound, M{"error": "missing build"}) |
| 208 | + return |
| 209 | + } |
| 210 | + writeJSON(w, http.StatusOK, build.Snapshot()) |
| 211 | + }) |
| 212 | + |
92 | 213 | mux.HandleFunc("GET /v0/builds", func(w http.ResponseWriter, r *http.Request) { |
| 214 | + if r.URL.Query().Get("project") == "" { |
| 215 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 216 | + return |
| 217 | + } |
93 | 218 | builds := make([]M, len(m.Builds)) |
94 | 219 | for i, b := range m.Builds { |
95 | 220 | builds[i] = b.Snapshot() |
@@ -171,21 +296,61 @@ func newServeMux(m *Mock) http.Handler { |
171 | 296 |
|
172 | 297 | if m.CompareBuild != nil { |
173 | 298 | mux.HandleFunc("POST /v0/builds/compare", func(w http.ResponseWriter, r *http.Request) { |
174 | | - if m.CompareBuild.PreviewBuild != nil { |
175 | | - m.CompareBuild.PreviewBuild.Reset() |
| 299 | + body := mustReadBody(r) |
| 300 | + if gjson.GetBytes(body, "project").String() == "" { |
| 301 | + writeJSON(w, http.StatusBadRequest, M{"error": "project is required"}) |
| 302 | + return |
| 303 | + } |
| 304 | + headBuild := m.CreateBuildFromTemplate(m.CompareBuild.PreviewBuild) |
| 305 | + if headBuild == nil { |
| 306 | + writeJSON(w, http.StatusNotFound, M{"error": "missing preview build"}) |
| 307 | + return |
176 | 308 | } |
| 309 | + head := cloneMap(m.CompareBuild.Head) |
| 310 | + head["id"] = headBuild.ID |
| 311 | + head["created_at"] = time.Now().Format(time.RFC3339) |
177 | 312 | writeJSON(w, http.StatusOK, M{ |
178 | 313 | "base": m.CompareBuild.Base, |
179 | | - "head": m.CompareBuild.Head, |
| 314 | + "head": head, |
180 | 315 | }) |
181 | 316 | }) |
182 | 317 | } |
183 | 318 |
|
| 319 | + mux.HandleFunc("POST /api/generate/spec", func(w http.ResponseWriter, r *http.Request) { |
| 320 | + body := mustReadBody(r) |
| 321 | + if gjson.GetBytes(body, "project").String() == "" || |
| 322 | + gjson.GetBytes(body, "source.openapi_spec").String() == "" || |
| 323 | + gjson.GetBytes(body, "source.stainless_config").String() == "" { |
| 324 | + writeJSON(w, http.StatusBadRequest, M{"error": "project, openapi_spec, and stainless_config are required"}) |
| 325 | + return |
| 326 | + } |
| 327 | + writeJSON(w, http.StatusOK, M{ |
| 328 | + "spec": M{ |
| 329 | + "diagnostics": M{}, |
| 330 | + }, |
| 331 | + }) |
| 332 | + }) |
| 333 | + |
184 | 334 | // Add simulated latency to all requests (except health checks). |
185 | 335 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 336 | + body := mustReadBody(r) |
| 337 | + m.RecordRequest(RecordedRequest{ |
| 338 | + Method: r.Method, |
| 339 | + Path: r.URL.Path, |
| 340 | + RawQuery: r.URL.RawQuery, |
| 341 | + Body: string(body), |
| 342 | + }) |
| 343 | + r.Body = io.NopCloser(bytes.NewReader(body)) |
| 344 | + |
186 | 345 | if r.URL.Path != "/health" { |
187 | 346 | time.Sleep(150 * time.Millisecond) |
188 | 347 | } |
189 | 348 | mux.ServeHTTP(w, r) |
190 | 349 | }) |
191 | 350 | } |
| 351 | + |
| 352 | +func mustReadBody(r *http.Request) []byte { |
| 353 | + body, _ := io.ReadAll(r.Body) |
| 354 | + r.Body = io.NopCloser(bytes.NewReader(body)) |
| 355 | + return body |
| 356 | +} |
0 commit comments