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

Replace lodash.get with JMESPath query #55

Merged
merged 6 commits into from Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 50 additions & 5 deletions README.md
Expand Up @@ -2,17 +2,17 @@

This action reads a YAML file and outputs selected properties.

The action uses lodash.get to access properties at the provided paths. The `file` input is the only required input. All other inputs are mapped into equally named outputs with the value at the given paths.
The action uses a [JMESPath](https://jmespath.org/) query to access properties at the provided paths. The `file` is the only input required. All other inputs are mapped into equally named outputs with the value at the given paths.

## Inputs

### `file`

**Required** The name of the file to load.

### `name: path`
### `name: query`

Give each path to look-up as a `name: path` input pair.
Give each path to look-up as a `name: query` input pair.

## Outputs

Expand All @@ -29,15 +29,32 @@ provider:
```

A step definition like this:
```yml
uses: CumulusDS/get-yaml-paths-action@v0.1.0
```yaml
uses: CumulusDS/get-yaml-paths-action@v0.2.0
with:
file: file.yml
bar: foo.bar
providerStage: provider.stage
```
sets the `bar` output to `baz` and sets the `providerstage` output (note all lower-case) to `green`.

Object and array outputs are JSON-serialized. For example, given an input file `qux.yml`:
```yaml
qux:
- bar: hello
- bar: world
```

A step definition like this:
```yaml
uses: CumulusDS/get-yaml-paths-action@v0.2.0
with:
file: qux.yml
bars: qux[].bar
```

Sets the `bars` output to `["hello","world"]`.

## CloudFormation

The CloudFormation YAML schema is supported. The input file can contain the CloudFormation template tags:
Expand All @@ -60,6 +77,34 @@ The CloudFormation YAML schema is supported. The input file can contain the Clou
- `!Split`
- `!Sub`

## Possible Issues

### Quoting Special Characters

Keys with hyphens and other special characters should be quoted in the query. For example, given an input file like this:
```yaml
foo-bar: baz
```

Then use a step definition like this:
```yaml
uses: CumulusDS/get-yaml-paths-action@v0.2.0
with:
file: file.yml
foobar: '"foo-bar"'
```

The query should be `"foo-bar"` to get the value of the `foo-bar` key. Without the quotes, the query would be interpreted as a numerical subtraction, which would result in an error.

### GitHub Runner Warning

The inputs to this action cannot be statically defined or listed in the actions.yml definition. This results in a nuisance warning from the Actions Runner like this:
```
Warning: Unexpected input(s) '...', valid inputs are ['file']
```

The [actions/runner#514](https://github.com/actions/runner/issues/514) issue tracks this limitation of the Actions Runner.

# See Also

[get-json-paths-action](https://github.com/gr2m/get-json-paths-action)
Expand Down
4 changes: 4 additions & 0 deletions env/test.yml
Expand Up @@ -2,3 +2,7 @@ foo:
bar: baz
provider:
stage: green
qux:
- bar: hello
- bar: world
foo-bar: 7
33 changes: 0 additions & 33 deletions flow-typed/npm/lodash.get_vx.x.x.js

This file was deleted.

8 changes: 4 additions & 4 deletions package.json
Expand Up @@ -50,12 +50,12 @@
}
},
"dependencies": {
"@actions/core": "^1.9.1",
"js-yaml": "^3.13.1",
"lodash.get": "^4.4.2"
"@actions/core": "^1.10.0",
"jmespath": "^0.16.0",
"js-yaml": "^3.13.1"
},
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/core": "^7.18.6",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/preset-flow": "^7.16.7",
Expand Down
8 changes: 5 additions & 3 deletions src/generateEachPath.js
@@ -1,14 +1,16 @@
// @flow

import get from "lodash.get";
// $FlowFixMe[untyped-import]
import jmespath from "jmespath";

export default function* generateEachPath(
document: {},
paths: { [string]: string }
): Iterable<{ name: string, value: string }> {
for (const name of Object.keys(paths)) {
const path = paths[name];
const value = get(document, path);
const query = paths[name];
const result = jmespath.search(document, query);
const value = typeof result === "object" ? JSON.stringify(result) : result;
yield { name, value };
}
}
1 change: 1 addition & 0 deletions src/main.js
Expand Up @@ -15,6 +15,7 @@ export default async function main() {
core.setOutput(name, value);
}
} catch (error) {
console.log(error);
core.setFailed(error.message);
process.exit(1);
}
Expand Down
13 changes: 12 additions & 1 deletion test/unit/main.test.js
Expand Up @@ -10,10 +10,21 @@ describe("main", () => {
const OLD_ENV = process.env;

it("calls setOutput when successful", async () => {
process.env = { INPUT_FILE: "env/test.yml", INPUT_A: "foo.bar", INPUT_B: "provider.stage", NOT_INPUT_C: "c" };
process.env = {
INPUT_FILE: "env/test.yml",
INPUT_A: "foo.bar",
INPUT_B: "provider.stage",
NOT_INPUT_C: "c",
INPUT_D: "qux[].bar",
INPUT_E: "foo",
INPUT_F: '"foo-bar"'
};
await main();
expect(core.setOutput).toHaveBeenCalledWith("a", "baz");
expect(core.setOutput).toHaveBeenCalledWith("b", "green");
expect(core.setOutput).toHaveBeenCalledWith("d", '["hello","world"]');
expect(core.setOutput).toHaveBeenCalledWith("e", '{"bar":"baz"}');
expect(core.setOutput).toHaveBeenCalledWith("f", 7);
});

it("calls setFailed when unsuccessful", async () => {
Expand Down
20 changes: 19 additions & 1 deletion test/unit/readYaml.test.js
Expand Up @@ -4,7 +4,25 @@ import readYaml from "../../src/readYaml";

describe("readYaml", () => {
it("reads a yaml file", () =>
expect(readYaml("env/test.yml")).resolves.toEqual({ foo: { bar: "baz" }, provider: { stage: "green" } }));
expect(readYaml("env/test.yml")).resolves.toMatchInlineSnapshot(`
Object {
"foo": Object {
"bar": "baz",
},
"foo-bar": 7,
"provider": Object {
"stage": "green",
},
"qux": Array [
Object {
"bar": "hello",
},
Object {
"bar": "world",
},
],
}
`));

it("reads a CloudFormation yaml file", () =>
expect(readYaml("env/cfn.yml")).resolves.toMatchObject({
Expand Down