Skip to content

Commit

Permalink
feat: parse checkboxes (#21)
Browse files Browse the repository at this point in the history
* feat: parse checkboxes

* fixup! feat: parse checkboxes

* docs: Update readme to match v3 behaviour

* fix: ensure every value sets an output parameter

* chore: update readme

Co-authored-by: Stefan Buck <github@stefanbuck.com>
  • Loading branch information
elhmn and stefanbuck committed Oct 19, 2022
1 parent 284e5eb commit 1d341cb
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 60 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![GitHub release](https://img.shields.io/github/release/stefanbuck/github-issue-parser.svg)](https://github.com/stefanbuck/github-issue-parser/releases) [![snyk](https://snyk.io/test/github/stefanbuck/github-issue-parser/badge.svg?targetFile=package.json)](https://snyk.io/test/github/stefanbuck/github-issue-parser?targetFile=package.json)

Use this action to convert issues into a unified JSON structure.
Use this action to convert issues into a unified JSON structure. Read the [Codeless Contributions with GitHub Issue Forms](https://stefanbuck.com/blog/codeless-contributions-with-github-issue-forms) post on my blog.

## Setup

Expand Down Expand Up @@ -59,6 +59,7 @@ body:
required: true
- type: checkboxes
id: what_else
attributes:
label: What else?
options:
Expand Down Expand Up @@ -99,8 +100,7 @@ The actions output will be
"what_happened": "A bug happened!",
"version": "1.0.0",
"browsers": "Chrome, Safari",
"never_give_up": true,
"hot_dog_is_a_sandwich": false
"what_else": ["Never give up"]
}
```

Expand Down
5 changes: 3 additions & 2 deletions fixtures/readme-example/expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"what_happened": "A bug happened!",
"version": "1.0.0",
"browsers": "Chrome, Safari",
"never_give_up": true,
"hot_dog_is_a_sandwich": false
"anything_else": ["Never give up"],
"second_anything_else": ["Hot Dog is a Sandwich", "Another item"],
"checkbox_without_an_id": []
}
17 changes: 17 additions & 0 deletions fixtures/readme-example/form.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,25 @@ body:
required: true

- type: checkboxes
id: anything_else
attributes:
label: What else?
options:
- label: Never give up
- label: Hot Dog is a Sandwich

- type: checkboxes
id: second_anything_else
attributes:
label: And with that?
options:
- label: Never give up
- label: Hot Dog is a Sandwich
- label: Another item

- type: checkboxes
attributes:
label: Checkbox without an id?
options:
- label: IDs are great
- label: IDs are bad
13 changes: 12 additions & 1 deletion fixtures/readme-example/issue-body.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,18 @@ A bug happened!

Chrome, Safari

### Code of Conduct
### What else?

- [x] Never give up
- [ ] Hot Dog is a Sandwich

### And with that?

- [ ] Never give up
- [x] Hot Dog is a Sandwich
- [x] Another item

### Checkbox without an id?

- [ ] IDs are great
- [ ] IDs are bad
85 changes: 67 additions & 18 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,24 @@ async function run(env, eventPayload, fs, core) {
if (item.type === "markdown") {
return null;
}
return [item.attributes.label, item.id];
return [item.attributes.label, item.id, item.type];
})
.filter(Boolean)
);
}

function getIDTypesFromIssueTemplate(formTemplate) {
if (!formTemplate.body) {
return {};
}

return Object.fromEntries(
formTemplate.body
.map((item) => {
if (item.type === "markdown") {
return null;
}
return [toKey(item.attributes.label), item.type];
})
.filter(Boolean)
);
Expand All @@ -41,6 +58,7 @@ async function run(env, eventPayload, fs, core) {
let result;
const body = eventPayload.issue.body || '';
const idMapping = getIDsFromIssueTemplate(form);
const idTypes = getIDTypesFromIssueTemplate(form);

function toKey(str) {
if (idMapping[str]) {
Expand All @@ -66,6 +84,33 @@ async function run(env, eventPayload, fs, core) {
return value;
}

//toObject merges checkbox results in an array and spits out correctly formatted object
function toObject(array) {
let result = {};

array.forEach((item) => {
const key = item && item[0];
const value = item && item[1];

if (key in result) {
const content = result[key];

if (value !== undefined) {
result[key] = content.concat(value);
}
return;
}

if (idTypes[key] == "checkboxes") {
result[key] = value === undefined ? [] : [value];
} else {
result[key] = value;
}
});

return result
}

result = body
.trim()
.split("###")
Expand All @@ -74,12 +119,18 @@ async function run(env, eventPayload, fs, core) {
return line
.split(/\r?\n\r?\n/)
.filter(Boolean)
.map((item) => {
.map((item, index, arr) => {
const line = item.trim();

if (line.startsWith("- [")) {
return line.split(/\r?\n/).map((check) => {
const field = check.replace(/- \[[X\s]\]\s+/i, "");
return [`${field}`, check.toUpperCase().startsWith("- [X] ")];
const previousIndex = index === 0 ? index : index - 1;
const key = arr[previousIndex].trim();
if (check.toUpperCase().startsWith("- [X] ")) {
return [key, field];
}
return [key];
});
}

Expand All @@ -92,32 +143,30 @@ async function run(env, eventPayload, fs, core) {
}

return [...prev, curr];
}, [])
.map(([key, ...lines]) => {
const checkListValue = lines.find((line) => Array.isArray(line));
const value = checkListValue
? toValue(checkListValue)
: toValue(...lines);

return [toKey(key), value];
});
}, []);

result.forEach(([key, value]) => {
core.setOutput(`issueparser_${key}`, value);
result = result.map(([key, ...lines]) => {
const checkListValue = lines.find((line) => Array.isArray(line));
const value = checkListValue ? toValue(checkListValue) : toValue(...lines);

return [toKey(key), value];
});

result = toObject(result);
Object.entries(result).forEach(([key, value]) => {
core.setOutput(`issueparser_${key}`, Array.isArray(value) ? value.join(',') : value);
})

function jsonStringify(json) {
return JSON.stringify(json, null, 2);
}

const json = Object.fromEntries(result);

fs.writeFileSync(
`${env.USERPROFILE || env.HOME}/issue-parser-result.json`,
jsonStringify(json),
jsonStringify(result),
"utf-8"
);
core.setOutput("jsonString", jsonStringify(json));
core.setOutput("jsonString", jsonStringify(result));
}

// We wrap the code in a `run` function to enable testing.
Expand Down
56 changes: 20 additions & 36 deletions test.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,21 @@ it("readme example", () => {

// mock core
const core = {
getInput(inputName) {
expect(inputName).toBe("template-path");
return "<template-path>";
},
setOutput(outputName, outputValue) {
if (outputName === "jsonString") {
expect(outputValue).toBe(expectedOutputJson);
return;
}

if (outputName.startsWith("issueparser_")) {
const key = outputName.substr("issueparser_".length);
expect(Object.keys(expectedOutput)).toContain(key);

expect(outputValue).toBe(expectedOutput[key]);
return;
}
},
getInput: jest.fn(() => '<template-path>'),
setOutput: jest.fn(),
};

run(env, eventPayload, fs, core);
expect(core.getInput).toHaveBeenCalledWith('template-path')
expect(core.setOutput).toHaveBeenCalledWith('jsonString', JSON.stringify(expectedOutput, null, 2))
expect(core.setOutput).toHaveBeenCalledWith('issueparser_contact', 'me@me.com')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_what_happened', 'A bug happened!')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_version', '1.0.0')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_browsers', 'Chrome, Safari')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_anything_else', 'Never give up')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_second_anything_else', 'Hot Dog is a Sandwich,Another item')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_checkbox_without_an_id', '')
expect(core.setOutput.mock.calls.length).toBe(8)
});

it("multiple paragraphs", () => {
Expand Down Expand Up @@ -84,27 +78,17 @@ it("multiple paragraphs", () => {

// mock core
const core = {
getInput(inputName) {
expect(inputName).toBe("template-path");
return "<template-path>";
},
setOutput(outputName, outputValue) {
if (outputName === "jsonString") {
expect(outputValue).toBe(expectedOutputJson);
return;
}

if (outputName.startsWith("issueparser_")) {
const key = outputName.substr("issueparser_".length);
expect(Object.keys(expectedOutput)).toContain(key);

expect(outputValue).toBe(expectedOutput[key]);
return;
}
},
getInput: jest.fn(() => '<template-path>'),
setOutput: jest.fn(),
};

run(env, eventPayload, fs, core);

expect(core.getInput).toHaveBeenCalledWith('template-path')
expect(core.setOutput).toHaveBeenCalledWith('jsonString', JSON.stringify(expectedOutput, null, 2))
expect(core.setOutput).toHaveBeenCalledWith('issueparser_textarea-one', '1st paragraph\n\n2nd paragraph')
expect(core.setOutput).toHaveBeenCalledWith('issueparser_textarea-two', '1st paragraph\n2nd paragraph')
expect(core.setOutput.mock.calls.length).toBe(3)
});

it("blank", () => {
Expand Down

0 comments on commit 1d341cb

Please sign in to comment.