Skip to content

Commit 4810d62

Browse files
authored
Merge pull request #9 from NYPL-discovery/pb/fix-schema-post
Fix bug uploading schema to schema service
2 parents ead1a53 + d853d86 commit 4810d62

4 files changed

Lines changed: 110 additions & 21 deletions

File tree

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,28 @@ client.get('patrons/12345678').then((resp) => {
7272

7373
To POST a new "TestSchema" schema:
7474
```js
75-
client.post('schemas/TestSchema', '{ "name": "TestSchema", "type": "record", "fields": [ ... ] }')
75+
client.post('schemas/TestSchema', { name: "TestSchema", type: "record", fields: [ ... ] })
7676
.then((resp) => {
7777
if (JSON.parse(resp).data.stream !== 'TestSchema') throw Error('Error creating schema...')
7878
})
7979
```
8080

8181
## CLI
8282

83-
A small CLI exists for common tasks. Run the following for a list of commands:
83+
A small CLI exists for common tasks.
84+
85+
If installed globally (i.e. `npm i -g @nypl/nypl-data-api-client`), it can be run as follows:
8486

8587
```js
8688
nypl-data-api
8789
```
8890

91+
For local installs, it can be run via local `node_modules`:
92+
93+
```js
94+
./node_modules/.bin/nypl-data-api
95+
```
96+
8997
To get help with any command run:
9098

9199
```js

bin/nypl-data-api.js

100644100755
Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,36 @@ const argv = require('minimist')(process.argv.slice(2))
1010
const DataApiClient = require('../')
1111
require('dotenv').config()
1212

13-
var client = new DataApiClient()
13+
const log_level = argv.log_level || 'error'
14+
15+
var client = new DataApiClient({ log_level })
1416

1517
var doPost = function (path, content) {
16-
console.log('client.post(' + path + ', ', JSON.stringify(content) + ')')
17-
client.post(path, JSON.stringify(content))
18+
19+
if ((typeof content) != 'object') {
20+
content = JSON.parse(content)
21+
}
22+
23+
client.post(path, content)
1824
.then((resp) => {
19-
console.log(`Done writing ${path}`)
25+
console.log(`Done POSTing to ${path}`)
26+
console.log(`Response: ${JSON.stringify(resp, null, 2)}`)
2027
}).catch((e) => {
2128
console.error('Error: ', e)
2229
})
2330
}
2431

2532
var doGet = function (path) {
26-
console.log('get path: ', path)
33+
if (!path) throw new Error('Path required')
34+
2735
client.get(path)
2836
.then((resp) => {
29-
console.log(JSON.stringify(resp, null, 2))
37+
let formattedResponse = resp
38+
if (typeof resp !== 'string') {
39+
// If JSON, format as json, otherwise print as-is:
40+
try { Object.keys(resp).length >= 1 && (formattedResponse = JSON.stringify(resp, null, 2)) } catch(e) { }
41+
}
42+
console.log('Got response: \n', formattedResponse)
3043
}).catch((e) => {
3144
console.error('Error: ', e)
3245
})
@@ -50,6 +63,30 @@ var showDiff = function (one, two) {
5063
console.log()
5164
}
5265

66+
function promptToPost (path, content) {
67+
if (!content) throw new Error('Posting requires content to post')
68+
69+
try {
70+
// Assume all posted bodies are json
71+
console.log("content: ", content)
72+
content = JSON.parse(content)
73+
74+
} catch (e) {
75+
throw new Error('Content to POST does not appear to be JSON. Only JSON supported currently.')
76+
}
77+
78+
console.log('POSTING')
79+
console.log(` To endpoint: ${process.env.NYPL_API_BASE_URL}${path}:`)
80+
console.log(` Using credentials: ${process.env.NYPL_OAUTH_KEY}@${process.env.NYPL_OAUTH_URL}`)
81+
console.log(`${JSON.stringify(content, null, 2)}`)
82+
console.log('Proceed?')
83+
prompt.start()
84+
prompt.get('y/n', (e, result) => {
85+
if (result['y/n'] === 'y') doPost(path, content)
86+
else console.log('Aborting.')
87+
})
88+
}
89+
5390
function schemaPost () {
5491
var name = argv._[2]
5592
var jsonfile = argv._[3]
@@ -91,15 +128,26 @@ function schemaPost () {
91128

92129
const commandHash = {
93130
schema: {
94-
post: {
95-
description: 'Post a version of a schema',
96-
usage: 'nypl-data-api schema post [name] [jsonfile]',
97-
exec: schemaPost
131+
description: 'Specialized commands for interacting with schemas',
132+
subcommands: {
133+
post: {
134+
description: 'Post a version of a schema. Will prepare request and confirm before proceeding.',
135+
usage: 'nypl-data-api schema post [name] [jsonfilepath]',
136+
examples: [ 'nypl-data-api schema post Bib ./new-bib-schema.json' ],
137+
exec: schemaPost
138+
}
98139
}
99140
},
141+
post: {
142+
description: 'Post JSON to an arbitrary endpoint. Will prepare request and confirm before proceeding.',
143+
usage: 'nypl-data-api post [path] [inlinejson]',
144+
examples: [ 'nypl-data-api post recap/checkin-requests \'{ "foo": "bar" }\'' ],
145+
exec: (argv.y ? doPost : promptToPost)
146+
},
100147
get: {
101-
description: 'Get arbitrary endpoint',
148+
description: 'Get arbitrary endpoint.',
102149
usage: 'nypl-data-api get [path]',
150+
examples: [ 'nypl-data-api schema get bibs' ],
103151
exec: doGet
104152
}
105153
}
@@ -112,7 +160,7 @@ function help (command, subcommand) {
112160
} else if (command && commandHash[command]) {
113161
if (commandHash[command].exec) commandSpec = commandHash[command]
114162
else {
115-
let subCommands = Object.keys(commandHash[command])
163+
let subCommands = Object.keys(commandHash[command].subcommands)
116164
console.log('Available subcommands: \n ' + subCommands.join('\n '))
117165
}
118166
} else {
@@ -121,9 +169,15 @@ function help (command, subcommand) {
121169
}
122170

123171
if (commandSpec) {
124-
console.log('Help: ' + command + (subcommand ? ' ' + subcommand : ''))
125-
console.log('Description: ' + commandSpec.description)
126-
console.log('Usage: ' + commandSpec.usage)
172+
console.log('Showing help for `' + command + (subcommand ? ' ' + subcommand : '') + '`')
173+
console.log(`Description: ${commandSpec.description}`)
174+
console.log(`Usage: ${commandSpec.usage}`)
175+
if (commandSpec.examples) {
176+
console.log('Examples:')
177+
commandSpec.examples.forEach((example) => {
178+
console.log(` $ ${example}`)
179+
})
180+
}
127181
}
128182
}
129183

@@ -137,16 +191,27 @@ else if (command === 'help') {
137191
help(_command, _subcommand)
138192
} else if (command) {
139193
try {
140-
let exec = commandHash[command][subcommand]
141-
let args = []
142-
if (!exec) {
143-
exec = commandHash[command].exec
194+
// Establish args to be passed to command:
195+
let args = argv._.slice(1)
196+
197+
// Establish which command group we'll be executing:
198+
let commandGroup = commandHash[command]
199+
200+
// If subcommand selected, use that:
201+
if (subcommand && commandGroup.subcommands && commandGroup.subcommands[subcommand]) {
202+
commandGroup = commandGroup.subcommands[subcommand]
144203
args = argv._.slice(1)
145204
}
205+
206+
// Execute the command
207+
exec = commandGroup.exec
146208
if (exec) exec.apply(null, args)
147209
else help(command)
210+
148211
} catch (e) {
149212
console.log('Error thrown: ', e)
213+
214+
// Show relevant help:
150215
help(command, subcommand)
151216
}
152217
}

lib/client.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ class Client {
196196
// Disallow caching anything but GET:
197197
if (method !== 'GET') options.cache = false
198198

199+
// Disallow non-object body if json enabled:
200+
if (options.json && options.body && (typeof options.body) !== 'object') {
201+
return Promise.reject(new Error(`Attempted to ${method} with options.json==true, but body is a ${typeof options.body}`))
202+
}
203+
199204
var uri = this._getFullUrl(path)
200205
var cacheKey = `${method} ${uri}`
201206

test/post-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ describe('Client POST method', function () {
2828
expect(resp).to.be.a('object')
2929
})
3030
})
31+
32+
it('should fail if supplied body is plaintext', function () {
33+
let call = client.post(`schemas/${testSchema.name}`, JSON.stringify(testSchema))
34+
return expect(call).to.be.rejected
35+
})
36+
37+
// A null/empty body should be accepted as valid if options.json===true
38+
it('should succeed if supplied body is empty', function () {
39+
let call = client.post(`schemas/${testSchema.name}`)
40+
return expect(call).to.be.fulfilled
41+
})
3142
})
3243

3344
describe('when config.json=false', function () {

0 commit comments

Comments
 (0)