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

feat: parse checkboxes #21

Merged
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GitHub Issue Parser

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 @@ -55,6 +55,7 @@ body:
required: true
- type: checkboxes
id: what_else
attributes:
label: What else?
options:
Expand Down Expand Up @@ -95,8 +96,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', '[email protected]')
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,25 +78,15 @@ 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)
});