Skip to content

Commit 04f2c86

Browse files
committed
fix!: make OP required in nested payload discriminated union
1 parent 13e6f11 commit 04f2c86

6 files changed

Lines changed: 18 additions & 22 deletions

File tree

docs/guide/adapters/standard-adapter/writing.md

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,32 @@ For association configuration, see [Associations](../../representations/associat
3535

3636
### Create
3737

38-
New records have no `id`:
39-
4038
```json
4139
{
4240
"invoice": {
4341
"number": "INV-001",
4442
"items": [
45-
{ "description": "Consulting" },
46-
{ "description": "Development" }
43+
{ "OP": "create", "description": "Consulting" },
44+
{ "OP": "create", "description": "Development" }
4745
]
4846
}
4947
}
5048
```
5149

5250
### Update
5351

54-
Existing records include `id`:
55-
5652
```json
5753
{
5854
"invoice": {
5955
"items": [
60-
{ "id": "5", "description": "Updated item" }
56+
{ "OP": "update", "id": "5", "description": "Updated item" }
6157
]
6258
}
6359
}
6460
```
6561

6662
### Delete
6763

68-
Include `id` and `OP: "delete"`:
69-
7064
```json
7165
{
7266
"invoice": {
@@ -87,8 +81,8 @@ Combine operations in one request:
8781
{
8882
"invoice": {
8983
"items": [
90-
{ "id": "5", "description": "Updated" },
91-
{ "description": "New item" },
84+
{ "OP": "update", "id": "5", "description": "Updated" },
85+
{ "OP": "create", "description": "New item" },
9286
{ "OP": "delete", "id": "3" }
9387
]
9488
}
@@ -104,9 +98,10 @@ Nesting can go to any depth:
10498
"invoice": {
10599
"items": [
106100
{
101+
"OP": "create",
107102
"description": "Consulting",
108103
"adjustments": [
109-
{ "amount": "10.00" }
104+
{ "OP": "create", "amount": "10.00" }
110105
]
111106
}
112107
]
@@ -120,18 +115,18 @@ TypeScript payloads use a discriminated union with `OP` as the discriminator:
120115

121116
```typescript
122117
interface ItemNestedCreatePayload {
123-
OP?: 'create';
118+
OP: 'create';
124119
description: string;
125120
}
126121

127122
interface ItemNestedUpdatePayload {
128-
OP?: 'update';
123+
OP: 'update';
129124
id?: string;
130125
description?: string;
131126
}
132127

133128
interface ItemNestedDeletePayload {
134-
OP?: 'delete';
129+
OP: 'delete';
135130
id: string;
136131
}
137132

@@ -141,7 +136,7 @@ type ItemNestedPayload =
141136
| ItemNestedDeletePayload;
142137
```
143138

144-
`OP` is optional on all variants. When omitted, the adapter infers the operation: records without `id` are created, records with `id` are updated.
139+
`OP` is the discriminator for the union. The adapter uses it to determine the operation for each nested record.
145140

146141
## Single Table Inheritance
147142

docs/guide/sorbus.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,12 @@ const { invoice } = await api.invoices.update({
130130
invoice: {
131131
items: [
132132
{
133+
OP: 'update',
133134
id: '5',
134135
description: 'Updated item',
135136
},
136137
{
138+
OP: 'create',
137139
description: 'New item',
138140
quantity: 1,
139141
rate: 100,
@@ -147,7 +149,7 @@ const { invoice } = await api.invoices.update({
147149
});
148150
```
149151

150-
No `id` means create. With `id` means update. `OP: 'delete'` with `id` means delete. All in one request, all typed.
152+
`OP` determines the operation: `'create'`, `'update'`, or `'delete'`.
151153

152154
## Error Handling
153155

lib/apiwork/adapter/standard/capability/writing/contract_builder.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def build_nested_payload(action_name)
9090
writable = action_name != :delete
9191

9292
object(type_name) do |object|
93-
object.literal(Constants::OP, optional: true, value: action_name.to_s)
93+
object.literal(Constants::OP, value: action_name.to_s)
9494
object.param(:id, optional: action_name != :delete, type: primary_key_type) unless action_name == :create
9595

9696
next unless writable

spec/integration/adapter/standard/capability/writing/nested_attributes_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
params: {
3939
invoice: {
4040
items: [
41-
{ description: 'Updated consulting', id: item1.id, invoice_id: invoice1.id, quantity: 20 },
41+
{ OP: 'update', description: 'Updated consulting', id: item1.id, invoice_id: invoice1.id, quantity: 20 },
4242
],
4343
},
4444
}
@@ -72,7 +72,7 @@
7272
params: {
7373
invoice: {
7474
items: [
75-
{ description: 'Updated consulting', id: item1.id, invoice_id: invoice1.id, quantity: 20 },
75+
{ OP: 'update', description: 'Updated consulting', id: item1.id, invoice_id: invoice1.id, quantity: 20 },
7676
{ OP: 'delete', id: item2.id },
7777
{ OP: 'create',
7878
description: 'Support contract',

spec/integration/adapter/standard/capability/writing/types_spec.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@
100100

101101
expect(param.type).to eq(:literal)
102102
expect(param.value).to eq('create')
103-
expect(param.optional?).to be(true)
104103
end
105104

106105
it 'does not include id' do

spec/integration/adapter/standard/validation_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
params: {
126126
invoice: {
127127
customer_id: customer.id,
128-
items: [{ description: '' }],
128+
items: [{ OP: 'create', description: '', invoice_id: 0, quantity: 1, unit_price: 100 }],
129129
number: 'INV-001',
130130
},
131131
}

0 commit comments

Comments
 (0)