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

3 ways to reference reusable components #181

Closed
RomanHotsiy opened this issue Apr 24, 2024 · 6 comments · Fixed by #182
Closed

3 ways to reference reusable components #181

RomanHotsiy opened this issue Apr 24, 2024 · 6 comments · Fixed by #182

Comments

@RomanHotsiy
Copy link

After reviewing the last draft of the spec I noticed there are 3 different ways to reference reusable components:

A simple object to allow referencing of objects contained within the Components Object from locations within steps or workflows in the Workflows Description. This excludes parameters and inputs defined in the components/parameters. Note - parameters should be referenced using the Reusable Parameter Object and inputs should use standard JSON Schema referencing using the $ref keyword.

We believe that the end users (authors) will be constantly confused about which to use in which context.

What problem do we solve with those 3 ways of referencing and why we can't use $refs for all of those (which users are familiar with as well as tooling support).

I believe it has to do with $ref semantics in context of JSON schema.
I believe while it may improve the experience for tooling authors it makes it much worse for the end users.

But I'm not even sure if it makes it easier for the tooling authors as now we have to deal with more error messages and validations.


On a separate note I'm not clear why the Reusable objects has this specific structure:

{
    "name": "{$components.successActions.notify}"
}

The moment that I'm unclear about is why the field is called name while the expression does not resolve to the name or anything like name?

@frankkilcommins
Copy link
Collaborator

@RomanHotsiy the 3 options have been grating a little with me too. I'm already thinking of consolidating Reusable Parameter Object into Reusable Object and only allow value overrides where the referenced object is a Parameter Object.

I can bite the bullet and rename name to reference too as part of the same effort. This was a mediocre attempt to stay even further away from a clash, but I now think that clear explaining how you reference a JSON Schema object versus a non JSON Schema object is clearer (also helped by just having two mechanisms rather than 3)

Removing the keyword clash with JSON Schema's $ref is indeed the primary motivation. There's a whole world of pain caused by this as well as real inabilities to adequately resolve references based on lack of clarity on how to deal with different JSON Schema versions in referenced fragments as well as dealing with the direction of resolution and upgrading or downgrading accordingly. It's completely left up to an implementor to decide which results in interoperability issues.

The same mantra of moving away from the current OAS Reference Object (which contains the keyword clash) is also being proposed for the next version of OAS.

@frankkilcommins
Copy link
Collaborator

frankkilcommins commented Apr 25, 2024

@RomanHotsiy - hopefully #182 simplifies the situation

@RomanHotsiy
Copy link
Author

@frankkilcommins this makes it better for sure! Thanks.

Yet I'm still confused why we have to invent a new way of reuse just for this spec. Whatever reusability mechanism is planned for Moonwalk it seems to be different form this.

So instead of a single reusability mechanism ($ref) we're inventing new ones for each spec.

I understand this is part of a broader discussion about $ref's semantics.

@handrews
Copy link
Contributor

handrews commented Apr 30, 2024

@RomanHotsiy the Moonwalk discussion isn't settled yet, and I'm paying attention to how this works out as we try to figure that out.

So instead of a single reusability mechanism ($ref) we're inventing new ones for each spec.

Because we don't have a single re-usability mechanism. We have at least 9 across 3.0 and 3.1 (6 in 3.0, 8 in 3.1, 5 overlapping):

  • OAS 3.0 Reference Object $ref (URI-reference, replaces context with target, ignores all adjacent properties)
  • OAS 3.x Path Item Object $ref (URI-reference, combines the target with the context, as long as no adjacent context properties conflict with target properties, otherwise the behavior is undefined)
  • OAS 3.1 Reference Object $ref (URI-reference, overrides target summary and description with context fields if present and replaces the context with the result; all other adjacent properties ignored)
  • OAS 3.1 Schema Object $ref (URI-reference, delegates to the target for the evaluation result, which is combined with adjacent keywords the same way as all other JSON Schema keywords)
  • OAS 3.1 Schema Object $dynamicRef (URI-reference with complex runtime resolution, otherwise delgates as Schema Object $ref)
  • OAS 3.x operationRef (URI-refernce, delegates to the target, mutually exclusive with operationId, does not otherwise interact with adjacent keywords)
  • OAS 3.x operationId (identifier searched across all Operation Objects in the API Description's scope, which can be ambigusous so there's a recommendation to avoid certain scenarios; delegaes to the target, mutually exclusive with operationRef, but no other interactions with adjacent keywords)
  • OAS 3.x Discriminator Object mapping (URI-reference OR component name, delegates to the named/referenced component, no interaction with adjacent keywords)
  • OAS 3.x Security Requirement Object keys (component name, delegates to the referenced component, no interaction with adjacent keys)

There are also numerous features that have implicit correlations - path or server template variables names, jsonSchemaDialect setting the default $schema, and probably other things I'm forgetting (the OASComply reports directory should have the complete taxonomy). EDIT: I was forgetting runtime expressions as used in 3.x as another thing. EDIT 2: And the way Server Objects get looked up across Operation, Path Item, and the root Object, which, like the component name connections, becomes ambiguous if you reference a complete document that has its own Components Object and/or root-level Servers Object).

@handrews
Copy link
Contributor

handrews commented Apr 30, 2024

This is not even getting into the fact that some of those linkages that use URI-references resolve as URLs (strictly location-based) and some in 3.1 resolve as URIs (identifiers that need not correlate to location).

@handrews
Copy link
Contributor

handrews commented Apr 30, 2024

Plus, tooling support for $ref in any form is egregiously inconsistent, and often outright incorrect. Too many tools refuse to process it, or refuse all but a few uses, or require its removal by a preprocessor. It's not like it's a system with good interoperability and consistent support. Nor does $ref meet the intuitive expectations of the many users who expect it to do some sort of merge, which none of the variations do.

$ref, with it's variable behavior and inconsistent support, is a huge pain point for consulting clients of mine that are trying to implement large-scale re-use across multiple providers. It's just not sufficiently interoperable or reliable, and the variations are too hard to understand. Workflows is specifically intended to span multiple API providers, and is likely to run into these problems. Without doing this, it was going to introduce at least one more $ref variation to that poorly-supported tangle of variations. Having recently implemented all forms of referencing (by URI-reference or component name or operationId) in both 3.0 and 3.1, throwing more complexity onto that fire really does not feel like a win to me.

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 a pull request may close this issue.

3 participants