Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental support for generating Lakeview widgets - oneOf and anyOf #774

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

nfx
Copy link
Contributor

@nfx nfx commented Jan 18, 2024

Changes

  • added const support
  • addednoConst field slice filters to facilitate {{- range .NonRequiredFields | alphanumOnly | noConst}} - most likely we don't need it, as we should better manipulate field maps in different passes
  • added constOnly to facilitate body = { {{range .Fields | constOnly}}'{{.Name}}': {{template "const" .Entity}},{{end}} }
  • added oneOf support with constant discriminators
  • added anyOf support with is_assignable functionality delegated to a target language.

Reference Python implementation of a supertype with constant discriminator resolution

Subtype name and version are decoupled from the actual type deserialization

class WidgetSpec(abc.ABC):
    @abc.abstractmethod
    def as_dict(self) -> Json:
        raise NotImplementedError

    @classmethod
    def from_dict(cls, d: Json) -> WidgetSpec:
        if d["version"] == 1 and d["widgetType"] == "details":
            return DetailsV1Spec.from_dict(d)
        elif d["version"] == 1 and d["widgetType"] == "table":
            return TableV1Spec.from_dict(d)
        #... 
        else:
            raise KeyError(f'unknown: version={d["version"]} widgetType={d["widgetType"]}')

Example Python implementation with constant discriminators

Type serialization silently encodes type/version metadata and ignores that on read

@dataclass
class CounterSpec(WidgetSpec):
    encodings: CounterEncodingMap
    format: FormatConfig | None = None
    frame: WidgetFrameSpec | None = None

    def as_dict(self) -> Json:
        body: Json = {
            "version": 2,
            "widgetType": "counter",
        }
        if self.encodings:
            body["encodings"] = self.encodings.as_dict()
        if self.format:
            body["format"] = self.format.as_dict()
        if self.frame:
            body["frame"] = self.frame.as_dict()
        return body

    @classmethod
    def from_dict(cls, d: Json) -> CounterSpec:
        return cls(
            encodings=_from_dict(d, "encodings", CounterEncodingMap),
            format=_from_dict(d, "format", FormatConfig),
            frame=_from_dict(d, "frame", WidgetFrameSpec),
        )

Loose supertype implementation in Python

pros:

  • we can emulate TypeScript's compile-time interface-on-read during runtime
  • we can support list[Foo | Bar] (which is essentially list[FooOrBar] && FooOrBar = Foo | Bar), but cannot support list[Foo] | list[Bar]

cons:

  • we don't know JSON-level field names, so we have to convert idiomatic field names_like_this to namesLikeThis on a per-module basis, if we want to support anyOf with loose typing
class ControlEncoding(abc.ABC):
    @abc.abstractmethod
    def as_dict(self) -> Json:
        raise NotImplementedError

    @classmethod
    def from_dict(cls, d: Json) -> ControlEncoding:
        reasons = []
        yes, why_not = _is_assignable(ControlFieldEncoding, d, [], _snake_to_camel)
        if yes:
            return ControlFieldEncoding.from_dict(d)
        if why_not:
            reasons.append(why_not)
        yes, why_not = _is_assignable(ParameterEncoding, d, [], _snake_to_camel)
        if yes:
            return ParameterEncoding.from_dict(d)
        if why_not:
            reasons.append(why_not)
        raise KeyError(" and ".join(reasons))

Tests

  • manual tests only

- add `const` support along with `constOnly` and `noConst` field slice filters to facilitate `{{- range .NonRequiredFields | alphanumOnly | noConst}}`
@codecov-commenter
Copy link

Codecov Report

Attention: 164 lines in your changes are missing coverage. Please review.

Comparison is base (aad9abf) 18.27% compared to head (d8bdf9b) 18.13%.

Files Patch % Lines
openapi/code/polymorphism.go 13.92% 132 Missing and 4 partials ⚠️
openapi/code/tmpl_util_funcs.go 0.00% 10 Missing ⚠️
openapi/code/package.go 42.85% 6 Missing and 2 partials ⚠️
openapi/model.go 0.00% 6 Missing ⚠️
openapi/code/load.go 60.00% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #774      +/-   ##
==========================================
- Coverage   18.27%   18.13%   -0.14%     
==========================================
  Files         110      112       +2     
  Lines       14834    15113     +279     
==========================================
+ Hits         2711     2741      +30     
- Misses      11901    12142     +241     
- Partials      222      230       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@nfx nfx changed the title Experimental support for generating Lakeview widgets Experimental support for generating Lakeview widgets - oneOf and anyOf Jan 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants