Skip to content

Instantly share code, notes, and snippets.

@mikekistler
Created January 28, 2025 03:27
Show Gist options
  • Save mikekistler/9f599ee145f9df0216ad2d7c4e57ac1e to your computer and use it in GitHub Desktop.
Save mikekistler/9f599ee145f9df0216ad2d7c4e57ac1e to your computer and use it in GitHub Desktop.
Overlay example with Traits

This is my attempt to get the Traits overlay example working with Speakeasy's overlay tooling.

The original example is here.

Original OpenAPI

openapi: 3.1.0
info:
  title: API with a paged collection
  version: 1.0.0
paths:
  /items:
    get:
      x-oai-traits: ['paged']
      responses:
        200:
          description: OK

Overlay

overlay: 1.0.0
info:
  title: Apply Traits
  version: 1.0.0
actions:
  - target: $.paths.*.get[[email protected]]
    update:
      parameters:
        - name: top
          in: query
          schema:
            type: integer
        - name: skip
          in: query
          schema:
            type: integer
@mikekistler
Copy link
Author

First problem is the hyphen in "x-oai-traits".

image

@mikekistler
Copy link
Author

mikekistler commented Jan 28, 2025

Switched to bracket notation

image

but now Speakeasy seems to want a special setting.

image

Looking at the source code, that message is coming from here:

if hasFilterExpression && overlay.JSONPathVersion != "rfc9535" {
	return "", fmt.Errorf("invalid overlay schema: must have `x-speakeasy-jsonpath: rfc9535`")
}

So you need this extension to get filter expressions to work. Interesting.

@mikekistler
Copy link
Author

mikekistler commented Jan 28, 2025

With this change, the errors are resolved but the overlay as written does not work as expected. It appears that the JSON path does not match any location in the document because the OpenAPI is unchanged.

Second try:

Original OpenAPI

openapi: 3.1.0
info:
  title: API with a paged collection
  version: 1.0.0
paths:
  /items:
    get:
      x-oai-paged: true
      responses:
        200:
          description: OK

Overlay

overlay: 1.0.0
x-speakeasy-jsonpath: rfc9535
info:
  title: Apply Traits
  version: 1.0.0
actions:
  - target: $.paths[[email protected] && @.get['x-oai-paged']]
    update:
      get:
        parameters:
          - name: top
            in: query
            schema:
              type: integer
          - name: skip
            in: query
            schema:
              type: integer

Here it is in the Overlay Playground: link

@mikekistler
Copy link
Author

Let's dig into the JSON Path RFC RFC 9535 to see if we can understand this, specifically the section on Filter Selectors.

Filter selectors are used to iterate over the elements or members of structured values, i.e., JSON arrays and objects.

Okay ... so filter selectors should work on objects ... but how do they work?

For each iteration (element/member), a logical expression (the filter expression) is evaluated, which decides whether the node of the element/member is selected.

So for objects, the filter is deciding which members of the object are selected, NOT which objects are selected based on its members. WOW.

Later (Section 2.3.5.2) it says:

The filter selector works with arrays and objects exclusively. Its result is a list of (zero, one, multiple, or all) their array elements or member values, respectively.

I think this confirms it -- filters select member values of an object, NOT the objects themselves.

@mikekistler
Copy link
Author

mikekistler commented Jan 28, 2025

What about the JSON Path Plus package? It has an online tool too:

https://jsonpath.com

Interesting that I see it offers two modes -- "RFC 9535" and "JSONPath Plus".

Of course, it only accepts JSON and not YAML, so here's the same schemas as above but in JSON:

Original OpenAPI

{
  "openapi": "3.1.0",
  "info": {
    "title": "API with a paged collection",
    "version": "1.0.0"
  },
  "paths": {
    "/items": {
      "get": {
        "x-oai-paged": true,
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  }
}

And let's try a few queries:

  • RFC 9535 mode

    • $.paths.*.get[[email protected]] - syntax error
    • $.paths.*.get[?@['x-oai-paged']] - no syntax error but no nodes selected
    • $.paths[[email protected] && @.get['x-oai-paged']] -- works and selects the expect object
  • JSONPath Plus mode

    • $.paths.*.get[[email protected]] - no syntax error but no nodes selected
    • $.paths.*.get[?@['x-oai-paged']] - no syntax error but no nodes selected
    • $.paths[[email protected] && @.get['x-oai-paged']] -- no syntax error but no nodes selected
    • $.paths.*.get[?(@.x-oai-paged)] - syntax error
    • $.paths.*.get[?(@['x-oai-paged'])] -- no syntax error but no nodes selected
    • $.paths.*.get[?(@['x-oai-paged'] == true)] -- no syntax error but no nodes selected
    • $.paths[?(@.get && @.get['x-oai-paged'])] -- works and selects the expect object

HMMMMM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment