Skip to content

Commit 1a408c4

Browse files
feat: Preserve credential field order
Stainless-Generated-From: 0df566e7c79bb73166acc226effc1c9f331264be
1 parent 8b22180 commit 1a408c4

8 files changed

Lines changed: 119 additions & 25 deletions

‎src/kernel/resources/vaults/items.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,9 @@ def upsert(
757757
Args:
758758
spec: Credential fields are for login and other non-payment credentials. Do not store,
759759
collect, or fill credit card data in credential items. Use wallet and card item
760-
types for credit cards and payment checkout instead.
760+
types for credit cards and payment checkout instead. Field order is preserved in
761+
the user-facing collection form, so list fields in the same top-to-bottom order
762+
as the website.
761763
762764
extra_headers: Send extra headers
763765
@@ -1528,7 +1530,9 @@ async def upsert(
15281530
Args:
15291531
spec: Credential fields are for login and other non-payment credentials. Do not store,
15301532
collect, or fill credit card data in credential items. Use wallet and card item
1531-
types for credit cards and payment checkout instead.
1533+
types for credit cards and payment checkout instead. Field order is preserved in
1534+
the user-facing collection form, so list fields in the same top-to-bottom order
1535+
as the website.
15321536
15331537
extra_headers: Send extra headers
15341538

‎src/kernel/types/vaults/credential_vault_field_definition.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88

99
class CredentialVaultFieldDefinition(BaseModel):
10+
name: str
11+
"""Stable field name used to key values, updates, and browser fills."""
12+
1013
required: bool
1114
"""Whether a nonempty value is required for readiness and form submission."""
1215

‎src/kernel/types/vaults/credential_vault_field_input_param.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111

1212
class CredentialVaultFieldInputParam(TypedDict, total=False):
13+
name: Required[str]
14+
"""Unique stable field name used to key values, updates, and browser fills."""
15+
1316
type: Required[CredentialVaultFieldType]
1417
"""
1518
Text, email, and password have form inputs; totp does not and is omitted from

‎src/kernel/types/vaults/credential_vault_item_request_param.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ class CredentialVaultItemRequestParam(TypedDict, total=False):
3030
"""Credential fields are for login and other non-payment credentials.
3131
3232
Do not store, collect, or fill credit card data in credential items. Use wallet
33-
and card item types for credit cards and payment checkout instead.
33+
and card item types for credit cards and payment checkout instead. Field order
34+
is preserved in the user-facing collection form, so list fields in the same
35+
top-to-bottom order as the website.
3436
"""
3537

3638
type: Required[Literal["credential"]]

‎src/kernel/types/vaults/credential_vault_item_spec.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

3-
from typing import Dict, Optional
3+
from typing import List, Optional
44

55
from ..._models import BaseModel
66
from .credential_vault_field_definition import CredentialVaultFieldDefinition
@@ -9,7 +9,10 @@
99

1010

1111
class CredentialVaultItemSpec(BaseModel):
12-
fields: Dict[str, CredentialVaultFieldDefinition]
12+
fields: List[CredentialVaultFieldDefinition]
13+
"""
14+
Ordered field definitions rendered in this order by credential collection forms.
15+
"""
1316

1417
description: Optional[str] = None
1518
"""

‎src/kernel/types/vaults/credential_vault_item_spec_input_param.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from typing import Dict
5+
from typing import Iterable
66
from typing_extensions import Required, TypedDict
77

88
from .credential_vault_field_input_param import CredentialVaultFieldInputParam
@@ -13,10 +13,15 @@
1313
class CredentialVaultItemSpecInputParam(TypedDict, total=False):
1414
"""Credential fields are for login and other non-payment credentials.
1515
16-
Do not store, collect, or fill credit card data in credential items. Use wallet and card item types for credit cards and payment checkout instead.
16+
Do not store, collect, or fill credit card data in credential items. Use wallet and card item types for credit cards and payment checkout instead. Field order is preserved in the user-facing collection form, so list fields in the same top-to-bottom order as the website.
1717
"""
1818

19-
fields: Required[Dict[str, CredentialVaultFieldInputParam]]
19+
fields: Required[Iterable[CredentialVaultFieldInputParam]]
20+
"""Ordered field definitions.
21+
22+
Use the website's top-to-bottom field order; the collection form renders this
23+
order unchanged.
24+
"""
2025

2126
description: str
2227
"""

‎src/kernel/types/vaults/item_upsert_params.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ class CredentialVaultItemRequest(TypedDict, total=False):
194194
"""Credential fields are for login and other non-payment credentials.
195195
196196
Do not store, collect, or fill credit card data in credential items. Use wallet
197-
and card item types for credit cards and payment checkout instead.
197+
and card item types for credit cards and payment checkout instead. Field order
198+
is preserved in the user-facing collection form, so list fields in the same
199+
top-to-bottom order as the website.
198200
"""
199201

200202
type: Required[Literal["credential"]]

‎tests/api_resources/vaults/test_items.py‎

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,7 +1144,14 @@ def test_method_upsert_overload_3(self, client: Kernel) -> None:
11441144
item = client.vaults.items.upsert(
11451145
key="x",
11461146
id_or_name="id_or_name",
1147-
spec={"fields": {"foo": {"type": "text"}}},
1147+
spec={
1148+
"fields": [
1149+
{
1150+
"name": "name",
1151+
"type": "text",
1152+
}
1153+
]
1154+
},
11481155
type="credential",
11491156
)
11501157
assert_matches_type(VaultItem, item, path=["response"])
@@ -1156,14 +1163,15 @@ def test_method_upsert_with_all_params_overload_3(self, client: Kernel) -> None:
11561163
key="x",
11571164
id_or_name="id_or_name",
11581165
spec={
1159-
"fields": {
1160-
"foo": {
1166+
"fields": [
1167+
{
1168+
"name": "name",
11611169
"type": "text",
11621170
"required": True,
11631171
"sensitive": True,
11641172
"value": "x",
11651173
}
1166-
},
1174+
],
11671175
"description": "description",
11681176
},
11691177
type="credential",
@@ -1176,7 +1184,14 @@ def test_raw_response_upsert_overload_3(self, client: Kernel) -> None:
11761184
response = client.vaults.items.with_raw_response.upsert(
11771185
key="x",
11781186
id_or_name="id_or_name",
1179-
spec={"fields": {"foo": {"type": "text"}}},
1187+
spec={
1188+
"fields": [
1189+
{
1190+
"name": "name",
1191+
"type": "text",
1192+
}
1193+
]
1194+
},
11801195
type="credential",
11811196
)
11821197

@@ -1191,7 +1206,14 @@ def test_streaming_response_upsert_overload_3(self, client: Kernel) -> None:
11911206
with client.vaults.items.with_streaming_response.upsert(
11921207
key="x",
11931208
id_or_name="id_or_name",
1194-
spec={"fields": {"foo": {"type": "text"}}},
1209+
spec={
1210+
"fields": [
1211+
{
1212+
"name": "name",
1213+
"type": "text",
1214+
}
1215+
]
1216+
},
11951217
type="credential",
11961218
) as response:
11971219
assert not response.is_closed
@@ -1209,15 +1231,29 @@ def test_path_params_upsert_overload_3(self, client: Kernel) -> None:
12091231
client.vaults.items.with_raw_response.upsert(
12101232
key="x",
12111233
id_or_name="",
1212-
spec={"fields": {"foo": {"type": "text"}}},
1234+
spec={
1235+
"fields": [
1236+
{
1237+
"name": "name",
1238+
"type": "text",
1239+
}
1240+
]
1241+
},
12131242
type="credential",
12141243
)
12151244

12161245
with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"):
12171246
client.vaults.items.with_raw_response.upsert(
12181247
key="",
12191248
id_or_name="id_or_name",
1220-
spec={"fields": {"foo": {"type": "text"}}},
1249+
spec={
1250+
"fields": [
1251+
{
1252+
"name": "name",
1253+
"type": "text",
1254+
}
1255+
]
1256+
},
12211257
type="credential",
12221258
)
12231259

@@ -2349,7 +2385,14 @@ async def test_method_upsert_overload_3(self, async_client: AsyncKernel) -> None
23492385
item = await async_client.vaults.items.upsert(
23502386
key="x",
23512387
id_or_name="id_or_name",
2352-
spec={"fields": {"foo": {"type": "text"}}},
2388+
spec={
2389+
"fields": [
2390+
{
2391+
"name": "name",
2392+
"type": "text",
2393+
}
2394+
]
2395+
},
23532396
type="credential",
23542397
)
23552398
assert_matches_type(VaultItem, item, path=["response"])
@@ -2361,14 +2404,15 @@ async def test_method_upsert_with_all_params_overload_3(self, async_client: Asyn
23612404
key="x",
23622405
id_or_name="id_or_name",
23632406
spec={
2364-
"fields": {
2365-
"foo": {
2407+
"fields": [
2408+
{
2409+
"name": "name",
23662410
"type": "text",
23672411
"required": True,
23682412
"sensitive": True,
23692413
"value": "x",
23702414
}
2371-
},
2415+
],
23722416
"description": "description",
23732417
},
23742418
type="credential",
@@ -2381,7 +2425,14 @@ async def test_raw_response_upsert_overload_3(self, async_client: AsyncKernel) -
23812425
response = await async_client.vaults.items.with_raw_response.upsert(
23822426
key="x",
23832427
id_or_name="id_or_name",
2384-
spec={"fields": {"foo": {"type": "text"}}},
2428+
spec={
2429+
"fields": [
2430+
{
2431+
"name": "name",
2432+
"type": "text",
2433+
}
2434+
]
2435+
},
23852436
type="credential",
23862437
)
23872438

@@ -2396,7 +2447,14 @@ async def test_streaming_response_upsert_overload_3(self, async_client: AsyncKer
23962447
async with async_client.vaults.items.with_streaming_response.upsert(
23972448
key="x",
23982449
id_or_name="id_or_name",
2399-
spec={"fields": {"foo": {"type": "text"}}},
2450+
spec={
2451+
"fields": [
2452+
{
2453+
"name": "name",
2454+
"type": "text",
2455+
}
2456+
]
2457+
},
24002458
type="credential",
24012459
) as response:
24022460
assert not response.is_closed
@@ -2414,14 +2472,28 @@ async def test_path_params_upsert_overload_3(self, async_client: AsyncKernel) ->
24142472
await async_client.vaults.items.with_raw_response.upsert(
24152473
key="x",
24162474
id_or_name="",
2417-
spec={"fields": {"foo": {"type": "text"}}},
2475+
spec={
2476+
"fields": [
2477+
{
2478+
"name": "name",
2479+
"type": "text",
2480+
}
2481+
]
2482+
},
24182483
type="credential",
24192484
)
24202485

24212486
with pytest.raises(ValueError, match=r"Expected a non-empty value for `key` but received ''"):
24222487
await async_client.vaults.items.with_raw_response.upsert(
24232488
key="",
24242489
id_or_name="id_or_name",
2425-
spec={"fields": {"foo": {"type": "text"}}},
2490+
spec={
2491+
"fields": [
2492+
{
2493+
"name": "name",
2494+
"type": "text",
2495+
}
2496+
]
2497+
},
24262498
type="credential",
24272499
)

0 commit comments

Comments
 (0)