|
8 | 8 | "encoding/json" |
9 | 9 | "fmt" |
10 | 10 | "io" |
| 11 | + "mime/multipart" |
11 | 12 | "net/http" |
12 | 13 | ) |
13 | 14 |
|
@@ -163,6 +164,83 @@ func (c *APIClient) UpdateFlow(ctx context.Context, pk string, updates map[strin |
163 | 164 | return nil |
164 | 165 | } |
165 | 166 |
|
| 167 | +// DeleteFlow removes a flow by PK. |
| 168 | +func (c *APIClient) DeleteFlow(ctx context.Context, pk string) error { |
| 169 | + url := fmt.Sprintf("%s/api/v3/flows/instances/%s/", c.BaseURL, pk) |
| 170 | + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) |
| 171 | + if err != nil { |
| 172 | + return fmt.Errorf("failed to create request: %w", err) |
| 173 | + } |
| 174 | + |
| 175 | + req.Header.Set("Authorization", "Bearer "+c.Token) |
| 176 | + |
| 177 | + resp, err := c.HTTPClient.Do(req) |
| 178 | + if err != nil { |
| 179 | + return fmt.Errorf("flow deletion request failed: %w", err) |
| 180 | + } |
| 181 | + defer func() { _ = resp.Body.Close() }() |
| 182 | + |
| 183 | + if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { |
| 184 | + body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024)) |
| 185 | + return fmt.Errorf("flow deletion failed with status %d: %s", resp.StatusCode, string(body)) |
| 186 | + } |
| 187 | + |
| 188 | + return nil |
| 189 | +} |
| 190 | + |
| 191 | +// DeleteFlowBySlug removes a flow if it exists. |
| 192 | +func (c *APIClient) DeleteFlowBySlug(ctx context.Context, slug string) error { |
| 193 | + flow, err := c.GetFlow(ctx, slug) |
| 194 | + if err != nil { |
| 195 | + return err |
| 196 | + } |
| 197 | + if flow == nil { |
| 198 | + return nil |
| 199 | + } |
| 200 | + return c.DeleteFlow(ctx, flow.PK) |
| 201 | +} |
| 202 | + |
| 203 | +// ImportFlow uploads a blueprint YAML definition for a flow. |
| 204 | +func (c *APIClient) ImportFlow(ctx context.Context, yaml []byte) error { |
| 205 | + url := fmt.Sprintf("%s/api/v3/flows/instances/import/", c.BaseURL) |
| 206 | + |
| 207 | + body := &bytes.Buffer{} |
| 208 | + writer := multipart.NewWriter(body) |
| 209 | + part, err := writer.CreateFormFile("file", "flow.yaml") |
| 210 | + if err != nil { |
| 211 | + return fmt.Errorf("failed to create multipart form: %w", err) |
| 212 | + } |
| 213 | + |
| 214 | + if _, err := part.Write(yaml); err != nil { |
| 215 | + return fmt.Errorf("failed to write flow blueprint: %w", err) |
| 216 | + } |
| 217 | + |
| 218 | + if err := writer.Close(); err != nil { |
| 219 | + return fmt.Errorf("failed to finalize multipart form: %w", err) |
| 220 | + } |
| 221 | + |
| 222 | + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, body) |
| 223 | + if err != nil { |
| 224 | + return fmt.Errorf("failed to create request: %w", err) |
| 225 | + } |
| 226 | + |
| 227 | + req.Header.Set("Authorization", "Bearer "+c.Token) |
| 228 | + req.Header.Set("Content-Type", writer.FormDataContentType()) |
| 229 | + |
| 230 | + resp, err := c.HTTPClient.Do(req) |
| 231 | + if err != nil { |
| 232 | + return fmt.Errorf("flow import request failed: %w", err) |
| 233 | + } |
| 234 | + defer func() { _ = resp.Body.Close() }() |
| 235 | + |
| 236 | + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent { |
| 237 | + body, _ := io.ReadAll(io.LimitReader(resp.Body, 2048)) |
| 238 | + return fmt.Errorf("flow import failed with status %d: %s", resp.StatusCode, string(body)) |
| 239 | + } |
| 240 | + |
| 241 | + return nil |
| 242 | +} |
| 243 | + |
166 | 244 | // CreateEnrollmentFlow creates a new enrollment flow |
167 | 245 | func (c *APIClient) CreateEnrollmentFlow(ctx context.Context, name, slug, title string) (*FlowResponse, error) { |
168 | 246 | reqBody := map[string]interface{}{ |
@@ -211,30 +289,6 @@ func (c *APIClient) CreateEnrollmentFlow(ctx context.Context, name, slug, title |
211 | 289 | return &flow, nil |
212 | 290 | } |
213 | 291 |
|
214 | | -// DeleteFlow deletes a flow by PK |
215 | | -func (c *APIClient) DeleteFlow(ctx context.Context, pk string) error { |
216 | | - url := fmt.Sprintf("%s/api/v3/flows/instances/%s/", c.BaseURL, pk) |
217 | | - req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) |
218 | | - if err != nil { |
219 | | - return fmt.Errorf("failed to create request: %w", err) |
220 | | - } |
221 | | - |
222 | | - req.Header.Set("Authorization", "Bearer "+c.Token) |
223 | | - |
224 | | - resp, err := c.HTTPClient.Do(req) |
225 | | - if err != nil { |
226 | | - return fmt.Errorf("flow deletion request failed: %w", err) |
227 | | - } |
228 | | - defer func() { _ = resp.Body.Close() }() |
229 | | - |
230 | | - if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK { |
231 | | - body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024)) |
232 | | - return fmt.Errorf("flow deletion failed with status %d: %s", resp.StatusCode, string(body)) |
233 | | - } |
234 | | - |
235 | | - return nil |
236 | | -} |
237 | | - |
238 | 292 | // GetFlowStages retrieves all stage bindings for a flow (ordered by binding order) |
239 | 293 | // Returns stages in execution order for the given flow |
240 | 294 | func (c *APIClient) GetFlowStages(ctx context.Context, flowPK string) ([]StageBindingResponse, error) { |
|
0 commit comments