Skip to content

Commit 9dace56

Browse files
committed
wb-revisions: accept ids on stdin
1 parent e703c89 commit 9dace56

File tree

5 files changed

+91
-52
lines changed

5 files changed

+91
-52
lines changed

bin/wb-revisions.js

+51-38
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { get } from '#lib/request'
99
import { isPositiveIntegerString } from '#lib/types'
1010
import { getWbk } from '#lib/wbk'
1111

12+
program.acceptsArgsOnStdin = true
13+
1214
await program
1315
.option('-s, --start <date>', 'start date')
1416
.option('-e, --end <date>', 'end date')
@@ -20,49 +22,60 @@ exitOnMissingInstance(program.instance)
2022

2123
const { getRevisions } = getWbk(program)
2224

23-
// Not parsing the ids with ../lib/tolerant_id_parser as that would
24-
// remove prefixes which are required for entities out of the main namespace
25-
// Ex: Property:P570
26-
const ids = program.args
27-
if (!(ids && ids.length > 0)) program.helpAndExit(0)
28-
29-
ids.forEach(id => {
30-
let [ prefix, entityId ] = id.split(':')
31-
if (entityId) {
32-
if (prefix !== 'Property' && prefix !== 'Item') {
33-
throw new Error(`invalid entity prefix: ${prefix}`)
25+
function fetchAndLogRevisions (ids) {
26+
ids.forEach(id => {
27+
let [ prefix, entityId ] = id.split(':')
28+
if (entityId) {
29+
if (prefix !== 'Property' && prefix !== 'Item') {
30+
throw new Error(`invalid entity prefix: ${prefix}`)
31+
}
32+
} else {
33+
entityId = prefix
3434
}
35-
} else {
36-
entityId = prefix
37-
}
38-
if (!isEntityId(entityId)) throw new Error(`invalid entity id: ${id}`)
39-
})
35+
if (!isEntityId(entityId)) throw new Error(`invalid entity id: ${id}`)
36+
})
4037

41-
const query = {}
42-
let { start, end, limit, props, verbose } = program
43-
if (isPositiveIntegerString(start)) start = parseInt(start)
44-
if (isPositiveIntegerString(end)) end = parseInt(end)
38+
const query = {}
39+
let { start, end, limit, props, verbose } = program
40+
if (isPositiveIntegerString(start)) start = parseInt(start)
41+
if (isPositiveIntegerString(end)) end = parseInt(end)
4542

46-
query.start = start
47-
query.end = end
48-
query.limit = limit
49-
if (props) query.prop = props?.split(',')
43+
query.start = start
44+
query.end = end
45+
query.limit = limit
46+
if (props) query.prop = props?.split(/[,|]/)
5047

51-
const getAndLogRevisions = id => {
52-
const url = getRevisions({ ids: [ id ], ...query })
53-
if (verbose) console.log(`revision query: ${id}`, url)
54-
return get(url)
55-
.then(body => values(body.query.pages)[0])
48+
const getAndLogRevisions = id => {
49+
const url = getRevisions({ ids: [ id ], ...query })
50+
if (verbose) console.log(`revision query: ${id}`, url)
51+
return get(url)
52+
.then(body => values(body.query.pages)[0])
53+
}
54+
55+
if (ids.length === 1) {
56+
getAndLogRevisions(ids[0])
57+
.then(data => console.log(JSON.stringify(data)))
58+
.catch(errors_.exit)
59+
} else {
60+
// Getting revisisions data individually to be able to pass parameters
61+
// cf https://github.com/maxlath/wikibase-sdk/blob/master/docs/get_revisions.md
62+
Promise.all(ids.map(getAndLogRevisions))
63+
.then(logNdjson)
64+
.catch(errors_.exit)
65+
}
5666
}
5767

58-
if (ids.length === 1) {
59-
getAndLogRevisions(ids[0])
60-
.then(data => console.log(JSON.stringify(data)))
61-
.catch(errors_.exit)
68+
// process.stdin.isTTY will be undefined if the process is receiving
69+
// its stdin from another process
70+
if (program.args.length === 0 && process.stdin.isTTY) {
71+
program.helpAndExit(0)
72+
} else if (program.args.length > 0) {
73+
// Not parsing the ids with ../lib/tolerant_id_parser as that would
74+
// remove prefixes which are required for entities out of the main namespace
75+
// Ex: Property:P570
76+
const ids = program.args
77+
fetchAndLogRevisions(ids)
6278
} else {
63-
// Getting revisisions data individually to be able to pass parameters
64-
// cf https://github.com/maxlath/wikibase-sdk/blob/master/docs/get_revisions.md
65-
Promise.all(ids.map(getAndLogRevisions))
66-
.then(logNdjson)
67-
.catch(errors_.exit)
79+
const { readIdsFromStdin } = await import('#lib/read_ids_from_stdin')
80+
readIdsFromStdin(fetchAndLogRevisions)
6881
}

docs/read_operations.md

+6
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,12 @@ wb r <entities ids>
545545
```sh
546546
wd revisions Q3548931
547547
wd r Q3548931
548+
wd r Property:P31
549+
550+
# The requested ids can also be passed from stdin
551+
echo 'Q1032158
552+
Q2665313
553+
Q5420639' | wd r
548554
```
549555

550556
Options:

lib/read_ids_from_stdin.js

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import split from 'split'
22
import through from 'through'
3+
import program from '#lib/program'
34

45
let ids = []
56

67
export function readIdsFromStdin (handleIdsBatch) {
7-
const write = function (id) {
8+
let count = 0
9+
async function write (id) {
810
id = id.trim()
911
if (id === '') return
12+
count++
1013
ids.push(id)
1114
if (ids.length < 50) return
1215

@@ -15,18 +18,23 @@ export function readIdsFromStdin (handleIdsBatch) {
1518
const batch = ids
1619
ids = []
1720

18-
handleIdsBatch(batch)
19-
.then(() => this.resume())
21+
await handleIdsBatch(batch)
22+
this.resume()
2023
}
2124

22-
const end = function () {
25+
async function end () {
26+
if (count === 0) program.helpAndExit(0)
2327
if (ids.length === 0) return this.emit('end')
24-
handleIdsBatch(ids)
25-
.then(this.emit.bind(this, 'end'))
28+
await handleIdsBatch(ids)
29+
this.emit('end')
2630
}
2731

2832
process.stdin
2933
.pipe(split(/\s/))
3034
.pipe(through(write, end))
3135
.on('error', console.error)
36+
37+
setTimeout(() => {
38+
if (count === 0) program.helpAndExit(0)
39+
}, 500)
3240
}

test/wb-data.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@ import { shellExec, wdTest } from '#test/lib/utils'
55
const attributes = [ 'pageid', 'ns', 'title', 'lastrevid', 'modified', 'type', 'id', 'labels', 'descriptions', 'aliases', 'claims', 'sitelinks' ]
66

77
describe('wb data', () => {
8-
// This test fails when run from a script as process.stdin.isTTY is undefined
9-
// and the script thus will listen for stdin. But it works when run manually,
10-
// and no other way to detect a stdin input was found
11-
12-
// it('should display help', async () => {
13-
// const { stdout } = await shellExec('./bin/wd.js data')
14-
// stdout.should.containEql('Usage:')
15-
// })
8+
it('should display help', async () => {
9+
const { stdout } = await shellExec('./bin/wd.js data')
10+
stdout.should.containEql('Usage:')
11+
})
1612

1713
it('<entity>', async () => {
1814
const { stdout } = await shellExec('./bin/wd.js data Q123456')

test/wb-revisions.js

+16
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,20 @@ describe('wb revisions', () => {
66
const { stdout } = await shellExec('./bin/wb.js revisions')
77
stdout.should.containEql('Usage:')
88
})
9+
10+
it('should accept ids as arguments', async () => {
11+
const { stdout } = await shellExec('./bin/wd.js revisions -n 2 Q66636975')
12+
const lines = stdout.split('\n')
13+
lines.length.should.equal(1)
14+
const history = JSON.parse(lines)
15+
history.revisions.length.should.equal(2)
16+
})
17+
18+
it('should accept ids on stdin', async () => {
19+
const { stdout } = await shellExec('echo Q66636975 | ./bin/wd.js revisions -n 2')
20+
const lines = stdout.split('\n')
21+
lines.length.should.equal(1)
22+
const history = JSON.parse(lines)
23+
history.revisions.length.should.equal(2)
24+
})
925
})

0 commit comments

Comments
 (0)