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

Support resolving JSON refs relative to the initial root #717

Closed
disposedtrolley opened this issue Oct 30, 2019 · 6 comments · Fixed by #797
Closed

Support resolving JSON refs relative to the initial root #717

disposedtrolley opened this issue Oct 30, 2019 · 6 comments · Fixed by #797
Assignees
Labels
enhancement New feature or request

Comments

@disposedtrolley
Copy link

disposedtrolley commented Oct 30, 2019

User story.
As a user, I want to specify all of my $ref values as relative paths from the root of the collection of my OAS files, so I don't need to use relative paths and backtracking (i.e. ../../) on very deeply nested OAS files.

Is your feature request related to a problem?
The ResolveRunner treats all $refs as being relative to the directory of the file where it's declared.

In the structure below, paths/v2/greetings/greetings.yaml needs to reference schemas/GreetingsObject.yaml. If the reference is declared as

$ref: /schemas/GreetingsObject.ymal

the resolve fails as it tries to target /paths/v2/greetings/schemas/GreetingsObject.yaml as it considered the paths/v2/greetings folder to be the parent.

├── paths
│   └── v2
│       └── greetings
│           ├── greetings.yaml
│           └── greetings_by_id.yaml
├── root.yaml
├── schemas
│   └── GreetingsObject.yaml

Describe the solution you'd like
Spectral should allow for $ref values to be resolved as relative paths where the parent is the directory at the root of the OAS collection. The root is the parent folder of the input file to the lint CLI command:

spectral lint project/root.yaml

which results in project as the parent for all relative $ref paths.

CC @nogates @davidlopezre

@disposedtrolley disposedtrolley changed the title Support resolving all JSON refs relative to the initial root Support resolving JSON refs relative to the initial root Oct 30, 2019
@P0lip
Copy link
Contributor

P0lip commented Nov 12, 2019

@danielgtaylor
Hey, sorry for responding so late.
Would a CLI flag be sufficient?

@P0lip P0lip added the enhancement New feature or request label Nov 12, 2019
@disposedtrolley
Copy link
Author

Would a CLI flag be sufficient?

That would be fantastic :) Even better if this could be exposed as an argument to the spectral.run() method in the JS module too.

@P0lip
Copy link
Contributor

P0lip commented Nov 13, 2019

@danielgtaylor
I believe the JS API may already support it via resolve options.

spectral.run(doc, {
  resolve: {
     documentUri: './paths/root.yaml' // base path. I know it's called documentUri, but any path can be used here :D
  },
});

Note - it must be any file under paths, not the paths directory itself.

@disposedtrolley
Copy link
Author

disposedtrolley commented Nov 19, 2019

@P0lip apologies for the delayed response.

I'm a bit confused by the usage -- which OAS file should I provide as the documentUri, just the root document which contains the references to the other OAS files? Thanks!

Edit:

I think I've figured it out. I can get Spectral working with split files but I had to pass in a custom file resolver when constructing the Spectral instance, i.e.

const { Resolver } = require("@stoplight/json-ref-resolver");

const customResolvers = new Resolver({
  resolvers: {
    file: {
      resolve: ref => {
        return new Promise((resolve, reject) => {
          const basePath = __dirname;
          const refPath = ref.path();
          fs.readFile(path.join(basePath, refPath), "utf8", (err, data) => {
            if (err) {
              reject(err);
            } else {
              resolve(data);
            }
          });
        });
      }
    }
  }
});

const spectral = new Spectral({ resolver: customResolvers });

Is it possible to update the documentation with this info?

@P0lip
Copy link
Contributor

P0lip commented Nov 19, 2019

Yup, it's certainly something we could document.
I believe we have a bit of information on resolvers, but I doubt we have any actual examples, since this is quite an advanced use case, but will add the example you pasted if you don't mind.
cc @philsturgeon

Regarding CLI - I believe the ideal solution would be to allow custom resolvers, so that you can fully customize it. I thought of a basePath CLI flag initially, but the longer I think of it, the more convinced I'm it won't cover all use cases.
What do you think?

@disposedtrolley
Copy link
Author

I thought of a basePath CLI flag initially, but the longer I think of it, the more convinced I'm it won't cover all use cases.

Yeah I agree. I thought about this yesterday and I've decided to revise the way we construct the $ref values to be relative to the document its declared in vs. the root of all the documents. So if the project looked like this:

├── paths
│   └── v2
│       └── greetings
│           ├── greetings.yaml
│           └── greetings_by_id.yaml
├── root.yaml
├── schemas
│   └── GreetingsObject.yaml

a reference in paths/v2/greetings/greeting.yaml to schemas/GreetingsObject.yaml used to look like

$ref: schemas/GreetingsObject.yaml

and now looks like:

$ref: ../../../schemas/GreetingsObject.yaml

which is a valid relative filesystem path. We used the original method because we thought it'd be easier for users to type, but we can make tooling to address this.

This works perfectly with Spectral using a custom resolver and passing in the extra params to run like you suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants