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

Add .csv parsing to template #729

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions home/modules/contribute/examples/example1.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Issue Type,Priority,Issue key,Summary,Labels,Component/s,Component/s,Status,Fix Version/s
Bug,Critical,CBL-4922,Lithium: Failure in OkHttp authenticator,,Java-Android,Kotlin-Android,Closed,3.0.15
Bug,Major,CBL-4921,Lithium: Lower the max size on the ClientTask thread pool to 8,,Java-Android,,Closed,3.0.15
Bug,Major,CBL-4839,Attachments/Blobs got deleted after compaction&re-sync,,,,Closed,3.0.15
Bug,Major,CBL-4799,Lithium: Database.exists should support the default directory,backport,Java-Android,,Closed,3.0.15
Bug,Major,CBL-4139,Lithium build error on Linux,,,,Closed,3.0.15
Bug,Major,CBL-4125,"Port Replicator test helpers, SG, SGTestUser, ReplParams to Lithium",,,,Closed,3.0.15
Bug,Major,CBL-4111,Allow docs failed with property encryption / decryption to be retried,,,,Closed,3.0.15
Bug,Major,CBL-3879,Failed test - testDoubleConflictResolutionOnSameConflicts,,,,Closed,3.0.15
Bug,Major,CBL-3878,testStopContinuousReplicator inconsistent failure,,,,Closed,3.0.15
Bug,Major,CBL-3877,testDeleteWithActiveReplicatorAndURLEndpointListeners failure,,,,Closed,3.0.15
Bug,Critical,CBL-3871,QueryParams fails to decode bool,,,,Closed,3.0.15
22 changes: 22 additions & 0 deletions home/modules/contribute/pages/extensions-template.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,25 @@ Description::

--

== CSV parsing

NOTE: The CSV function is designed to handle the export from JIRA which handles lists ("Components" etc.) by producing multiple columns each called "Component/s".

[source,handlebars]
--
{{#each this}}
{{this.[Issue key]}}::
{{#each this.[Component/s]}}* {{.}}
{{/each}}
{{/each}}
--


[template,example$example1.csv]
--
{{#each this}}
{{this.[Issue key]}}::
{{#each this.[Component/s]}}* {{.}}
{{/each}}
{{/each}}
--
13 changes: 13 additions & 0 deletions lib/helpers/filter-by-component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict'

module.exports = filterByComponent

function filterByComponent(arr, ...rest) {
const components = rest.slice(0,-1) // last item is Handlebars context
return arr.filter(item => intersects(item["Component/s"], components))
}

function intersects(a, b) {
const setB = new Set(b);
return a.some(x => setB.has(x));
}
48 changes: 44 additions & 4 deletions lib/template-block.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ function setupHandlebars(attributes, config) {
// get a resource from the Antora contentCatalog
// This is called from the context of the current page,
// and attempts to process known filetypes:
// .json / .yml (parse)
// .js (require)
// everything else (just return string)
// .json / .yml / .csv (parse)
// .js (require)
// everything else (just return string)
//
//
function getResource (resourceId, config) {
Expand All @@ -113,22 +113,62 @@ function getResource (resourceId, config) {
else if (/\.ya?ml$/.test(resourceId)) {
return YAML.parse(target.contents.toString(), {maxAliasCount: -1} )
}
else if (/\.csv$/.test(resourceId)) {
return parse_csv(target.contents)
}
else if (/\.js$/.test(resourceId)) {
return requireFromString(target.contents.toString(), resourceId)
}
else {
console.log(target.contents.toString())
return target.contents.toString()
}
}

// Helper functions

/* This parse function is designed to handle the export from JIRA
* which will deal with lists ("Labels" etc.) by producing multiple columns
* each called "Label/s"
*
* `csv-parse` handles this with the `group_columns_by_name` option.
* However, in the case where there is only a *single* entry, the parser does
* not know to group the single result in an array.
* To make the output consistent each time, we post-process by wrapping the value
* of any fields named like "Foo/s" in an array if it is not already an array.
* Additionally, it's possible that not all rows will have the same number of items
* in the array. The handler will parse these as empty strings, so we will filter
* those out.
*/

function parse_csv(contents) {
const { parse } = require('csv-parse/sync')
const records = parse(
contents,
{
columns: true,
group_columns_by_name: true,
}
).map(
record => Object.fromEntries(
Object.entries(record).map(([key,val]) => {
if (key.endsWith("/s")) {
const arr = Array.isArray(val) ? val.filter(item => item !== '') : [val]
return [key, arr]
}
else {
return [key, val]
}})))

return records
}

// https://stackoverflow.com/questions/17581830/load-node-js-module-from-string-in-memory
function requireFromString(src, filename) {
var Module = module.constructor;
var m = new Module();
m._compile(src, filename);
return m.exports;
}
}

module.exports = { register }
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"antora": "~3.1",
"asciidoctor-external-callout": "~1.2.0",
"asciidoctor-kroki": "0.15.4",
"csv-parse": "5.5.0",
"gulp": "~4.0",
"gulp-connect": "~5.7",
"js-yaml": "~4.1",
Expand Down