Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions codegen/layouts/partials/resource-dataclass.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@
{{#each properties}}
{{../memberIndent}}{{pythonIdentifier name}}: {{type}}
{{/each}}
{{#if @root.hasRawJson}}
{{#unless isNested}}
{{memberIndent}}_raw: Optional[Dict[str, Any]] = field(default=None, repr=False, compare=False)

{{memberIndent}}def raw_json(self) -> str:
{{memberIndent}} """Return the payload this event was parsed from, as JSON."""
{{memberIndent}} return json.dumps(self._raw)
{{/unless}}
{{/if}}

{{memberIndent}}@classmethod
{{memberIndent}}def from_dict(cls, d: Any):
Expand All @@ -35,4 +44,9 @@
{{#each properties}}
{{../memberIndent}} {{pythonIdentifier name}}={{#if isRequiredObject}}cls.{{nestedClassName}}.from_dict(d.get("{{name}}") or {}){{else}}{{#if isObject}}cls.{{nestedClassName}}.from_dict(d.get("{{name}}")) if d.get("{{name}}") is not None else None{{else}}{{#if isDiscriminatedObjectList}}[_from_discriminated_dict(i, cls._{{nestedClassName}}Variants, "{{discriminator}}") for i in d.get("{{name}}") or []]{{else}}{{#if isObjectList}}[cls.{{nestedClassName}}.from_dict(i) for i in d.get("{{name}}") or []]{{else}}{{#if isDictParam}}DeepAttrDict({{/if}}d.get("{{name}}", None){{#if isDictParam}}){{/if}}{{/if}}{{/if}}{{/if}}{{/if}},
{{/each}}
{{#if @root.hasRawJson}}
{{#unless isNested}}
{{memberIndent}} _raw=d,
{{/unless}}
{{/if}}
{{memberIndent}} )
10 changes: 6 additions & 4 deletions codegen/layouts/resource.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from typing import Any, Dict, List, Literal, Optional, {{#if union.secondaryDiscriminator}}Tuple, {{/if}}Union{{#if union}}, cast{{/if}}
from dataclasses import dataclass
from dataclasses import dataclass{{#if hasRawJson}}, field{{/if}}
{{#if hasRawJson}}
import json
{{/if}}
from ..deep_attr_dict import DeepAttrDict
from ..resource_mapping import ResourceMapping

Expand Down Expand Up @@ -36,11 +39,10 @@ def _from_discriminated_dict(
def {{union.fromDictName}}(d: Any) -> {{union.className}}:
"""Deserialize a known {{union.discriminator}}{{#if union.secondaryDiscriminator}} and {{union.secondaryDiscriminator}}{{/if}} variant.

Unknown discriminator values return ``DeepAttrDict`` so payloads from a
newer API remain readable. The static return type covers known variants.
An unrecognized {{union.discriminator}} yields {{#if union.fallbackClassName}}``{{union.fallbackClassName}}``{{else}}``DeepAttrDict``{{/if}}.
"""
variant = {{union.variantsName}}.get({{#if union.secondaryDiscriminator}}(d.get("{{union.discriminator}}"), d.get("{{union.secondaryDiscriminator}}")){{else}}d.get("{{union.discriminator}}"){{/if}})
if variant is None:
return cast({{union.className}}, DeepAttrDict(d))
return {{#if union.fallbackClassName}}cast({{union.className}}, {{union.fallbackClassName}}.from_dict(d)){{else}}cast({{union.className}}, DeepAttrDict(d)){{/if}}
return variant.from_dict(d)
{{/if}}
82 changes: 71 additions & 11 deletions codegen/lib/layouts/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {

export interface ResourceLayoutContext {
className: string
hasRawJson?: boolean
moduleName: string
isDeprecated: boolean
deprecationMessage: string
Expand Down Expand Up @@ -501,6 +502,27 @@ const buildUnionAliases = (
}))
}

const commonScalarProperties = (variants: UnionVariant[]): Property[] => {
const [first, ...rest] = variants
if (first == null) return []

return first.properties
.filter((property) => {
if (property.format === 'list') return false
return rest.every((variant) =>
variant.properties.some(
({ name, format }) =>
name === property.name && format === property.format,
),
)
})
.map((property) =>
property.format === 'object'
? ({ ...property, format: 'record' } as unknown as Property)
: property,
)
}

const buildUnionResource = (
className: string,
discriminator: string,
Expand All @@ -509,6 +531,7 @@ const buildUnionResource = (
isDeprecated: boolean,
deprecationMessage: string,
secondaryDiscriminator?: string,
fallback?: { properties: Property[]; description: string },
): ResourceLayoutContext => {
const suffix = className === 'SeamEvent' ? 'Event' : 'ActionAttempt'
const classes = variants.map((variant) => {
Expand Down Expand Up @@ -550,6 +573,24 @@ const buildUnionResource = (
suffix,
),
]
const fallbackClassName = `Unrecognized${suffix}`
if (fallback != null) {
classes.push({
...buildClass(
fallbackClassName,
fallback.description,
fallback.properties.map((property) => ({
...property,
isOptional: true,
})),
snakeCase(className),
rootIndentation,
),
isDeprecated: false,
deprecationMessage: '',
})
}

const classNames = new Set(classes.map(({ className: name }) => name))
for (const alias of aliases) {
if (classNames.has(alias.className) || alias.className === className) {
Expand All @@ -565,14 +606,17 @@ const buildUnionResource = (
...(secondaryDiscriminator == null ? {} : { secondaryDiscriminator }),
fromDictName,
variantsName: `_${snakeCase(className).toUpperCase()}_VARIANTS`,
variants: classes.map((variantClass, index) => {
const secondaryValue = variants[index]?.secondaryValue
return {
className: variantClass.className,
values: [variants[index]?.value ?? ''],
...(secondaryValue == null ? {} : { secondaryValue }),
}
}),
...(fallback == null ? {} : { fallbackClassName }),
variants: classes
.filter(({ className: name }) => name !== fallbackClassName)
.map((variantClass, index) => {
const secondaryValue = variants[index]?.secondaryValue
return {
className: variantClass.className,
values: [variants[index]?.value ?? ''],
...(secondaryValue == null ? {} : { secondaryValue }),
}
}),
aliases,
}

Expand Down Expand Up @@ -703,8 +747,8 @@ export const getResourceLayoutContexts = (
const eventModel = blueprint.resources.find(
({ resourceType }) => resourceType === 'event',
)
resources.push(
buildUnionResource(
resources.push({
...buildUnionResource(
'SeamEvent',
'event_type',
'seam_event_from_dict',
Expand All @@ -717,8 +761,17 @@ export const getResourceLayoutContexts = (
})),
eventModel?.isDeprecated ?? false,
eventModel?.deprecationMessage ?? '',
undefined,
eventModel == null
? undefined
: {
properties: eventModel.properties,
description:
'An event whose event_type this SDK version does not recognize.',
},
),
)
hasRawJson: true,
})

const actionAttemptModel = blueprint.resources.find(
({ resourceType }) => resourceType === 'action_attempt',
Expand All @@ -732,6 +785,13 @@ export const getResourceLayoutContexts = (
actionAttemptModel?.isDeprecated ?? false,
actionAttemptModel?.deprecationMessage ?? '',
'status',
{
properties: commonScalarProperties(
blueprint.actionAttempts.flatMap(expandActionAttemptByStatus),
),
description:
'An action attempt whose action_type or status this SDK version does not recognize.',
},
),
)

Expand Down
2 changes: 2 additions & 0 deletions seam/resources/__init__.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 33 additions & 3 deletions seam/resources/action_attempt.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading