|
| 1 | +# Connection Upsert Command Design |
| 2 | + |
| 3 | +## 1. Command Overview |
| 4 | + |
| 5 | +### Purpose and Use Cases |
| 6 | +The `hookdeck connection upsert` command provides an idempotent mechanism for creating or updating connections. It is designed for users who manage their webhook infrastructure as code, enabling them to define the desired state of a connection without worrying about its current state. |
| 7 | + |
| 8 | +- **Primary Use Case:** Declarative connection management in CI/CD pipelines or local scripts. |
| 9 | +- **Secondary Use Case:** A convenient way to make partial updates to a connection without fetching its full state. |
| 10 | + |
| 11 | +### Comparison with `create` |
| 12 | +- `hookdeck connection create`: Explicitly creates a new connection. Fails if a connection with the same name already exists. Use this when you intend to create a new resource and want to avoid accidental overwrites. |
| 13 | +- `hookdeck connection upsert`: Creates a connection if it doesn't exist by name, or updates it if it does. Use this for idempotent operations where the end state is what matters. |
| 14 | + |
| 15 | +## 2. Command Signature |
| 16 | + |
| 17 | +The command will use the connection `name` as a required positional argument, making it distinct and easy to use. |
| 18 | + |
| 19 | +```bash |
| 20 | +hookdeck connection upsert <name> [flags] |
| 21 | +``` |
| 22 | + |
| 23 | +## 3. Flag Definitions |
| 24 | + |
| 25 | +The `upsert` command will reuse all flags from the `connection create` command to ensure full control over connection properties. A new `--dry-run` flag will be added to preview changes. |
| 26 | + |
| 27 | +- **All flags will be optional.** When updating, only the provided flags will modify the connection's properties. |
| 28 | +- **`--dry-run` (boolean):** If present, the command will not perform the final `PUT` request. Instead, it will output a summary of the intended action (create or update) and the data that would be sent. |
| 29 | + |
| 30 | +## 4. Dry-Run Behavior |
| 31 | + |
| 32 | +The `--dry-run` flag is critical for safely managing infrastructure. |
| 33 | + |
| 34 | +### Implementation |
| 35 | +1. The command will first make a `GET /connections?name=<name>` API call to check if a connection with the given name exists. |
| 36 | +2. Based on the result, it will determine whether the operation is a **CREATE** or an **UPDATE**. |
| 37 | +3. It will construct the full request payload just as it would for a real operation. |
| 38 | +4. It will output a summary indicating the action and the payload. |
| 39 | + |
| 40 | +### Output Format |
| 41 | +The dry-run output will be a human-readable text summary, with an option for JSON output for scripting. |
| 42 | + |
| 43 | +**Text Summary Example (Create):** |
| 44 | +``` |
| 45 | +-- Dry Run: CREATE -- |
| 46 | +A new connection named 'my-connection' will be created with the following properties: |
| 47 | +- Source: my-source (inline) |
| 48 | +- Destination: my-destination (inline) |
| 49 | +- Rules: |
| 50 | + - Retry: exponential, 3 attempts |
| 51 | +``` |
| 52 | + |
| 53 | +**Text Summary Example (Update):** |
| 54 | +``` |
| 55 | +-- Dry Run: UPDATE -- |
| 56 | +Connection 'my-connection' (conn_123) will be updated with the following changes: |
| 57 | +- Description: "New description" |
| 58 | +- Rules: (ruleset will be replaced) |
| 59 | + - Filter: body contains '{"$.type":"payment"}' |
| 60 | +``` |
| 61 | + |
| 62 | +**JSON Output (`--output json`):** |
| 63 | +```json |
| 64 | +{ |
| 65 | + "action": "CREATE", |
| 66 | + "connection_name": "my-connection", |
| 67 | + "payload": { |
| 68 | + "name": "my-connection", |
| 69 | + "source": { "...etc" }, |
| 70 | + "destination": { "...etc" } |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +## 5. Behavior Specification |
| 76 | + |
| 77 | +- **When connection doesn't exist:** The command behaves like `create`. It will create a new connection using the provided flags. `source` and `destination` (either by ID or inline) will be required in this case. |
| 78 | +- **When connection exists:** The command will perform an update. Only the properties corresponding to the provided flags will be changed. |
| 79 | +- **When no properties provided:** If the connection exists and no flags are provided to modify it, the command will be a **no-op**. It will print a message indicating that no changes are needed. This is not an error. |
| 80 | +- **Rule Handling:** If *any* `--rule-*` flag or `--rules-file`/`--rules` is provided, the entire existing ruleset on the connection will be **replaced** with the new rules defined in the command. This ensures declarative and idempotent rule management. |
| 81 | + |
| 82 | +## 6. Validation Rules |
| 83 | + |
| 84 | +Validation logic must accommodate both create and update scenarios. |
| 85 | + |
| 86 | +1. **Initial Check:** The command will first check if the connection exists via a `GET` request. |
| 87 | +2. **Create Scenario Validation:** If the connection does not exist: |
| 88 | + - `source` (via `--source-id` or inline flags) is required. |
| 89 | + - `destination` (via `--destination-id` or inline flags) is required. |
| 90 | + - All other validation from `connection create` applies. |
| 91 | +3. **Update Scenario Validation:** If the connection exists: |
| 92 | + - All flags are optional. |
| 93 | + - Validation will only be performed for the flags that are provided. |
| 94 | + |
| 95 | +This approach avoids complex client-side logic and leverages the API for validation where appropriate, while providing helpful early feedback to the user. |
| 96 | + |
| 97 | +## 7. Usage Examples |
| 98 | + |
| 99 | +```bash |
| 100 | +# Create a new connection if 'my-connection' does not exist |
| 101 | +hookdeck connection upsert my-connection \ |
| 102 | + --source-name "my-source" --source-type STRIPE \ |
| 103 | + --destination-name "my-api" --destination-type HTTP --destination-url "https://example.com" |
| 104 | + |
| 105 | +# Update the rules for an existing connection, leaving other properties untouched |
| 106 | +hookdeck connection upsert my-connection \ |
| 107 | + --rule-retry-strategy linear --rule-retry-count 5 |
| 108 | + |
| 109 | +# Preview an update with --dry-run |
| 110 | +hookdeck connection upsert my-connection \ |
| 111 | + --description "A new description" --dry-run |
| 112 | + |
| 113 | +# No-op: connection exists, no flags provided |
| 114 | +hookdeck connection upsert my-connection |
| 115 | +``` |
| 116 | + |
| 117 | +## 8. Implementation Guidance |
| 118 | + |
| 119 | +- **Shared Code:** Create a shared `internal/connection` package or a `connection_shared.go` file to house common logic for flag definitions, payload construction, and rule parsing, to be used by both `create` and `upsert` commands. |
| 120 | +- **API Client:** Add a new `UpsertConnection` method to the API client that makes a `PUT /connections` call. The method should accept the connection name and the upsert payload. |
| 121 | +- **Dry-Run Logic:** The core logic for the dry run will live in the `upsert` command's `RunE` function. It will perform the initial `GET` request and then branch to either display the dry-run output or call the `UpsertConnection` method. |
| 122 | +- **Removing `update` command:** Delete `pkg/cmd/connection_update.go` and remove its registration from the parent `connection` command. |
| 123 | + |
| 124 | +## 9. Migration Strategy |
| 125 | + |
| 126 | +- **Remove `update` command:** The `connection update` command will be completely removed. |
| 127 | +- **Documentation:** Update `REFERENCE.md` and all other documentation to replace `update` with `upsert`, providing clear examples. |
| 128 | +- **CHANGELOG:** Add an entry under "Breaking Changes" announcing the replacement of `connection update` with `connection upsert` and explaining the benefits. |
| 129 | +- **User Communication:** Announce the change in release notes, highlighting the new idempotent capabilities and `--dry-run` support. |
| 130 | + |
| 131 | +## 10. Testing Strategy |
| 132 | + |
| 133 | +- **Create Behavior:** Write a test case where the connection name does not exist, and verify that it is created correctly. |
| 134 | +- **Update Behavior:** Test that when a connection exists, providing a subset of flags only updates those properties. |
| 135 | +- **Idempotency:** Run the same `upsert` command twice and verify that the state is the same after both runs and the second run reports a no-op. |
| 136 | +- **Dry-Run Accuracy:** For both create and update scenarios, test that the `--dry-run` output accurately reflects the action and payload. |
| 137 | +- **Rule Replacement:** Test that providing rule flags completely replaces the existing ruleset on a connection. |
0 commit comments