Skip to content

Commit

Permalink
Merge pull request #18 from NYPL-discovery/noref-improvements
Browse files Browse the repository at this point in the history
Improvements related to schema posting, patching
  • Loading branch information
nonword authored Dec 7, 2021
2 parents 538e5a0 + 7342e20 commit 36dcc83
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: node_js
install: npm install
script: npm test
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,15 @@ To get help with any command run:
nypl-data-api help [command]
```

Note that the lib draws from the following environment variables, which you can place in `.env`:
Note that the cli uses the following environment variables, read by default from `.env`:

- NYPL_API_BASE_URL
- NYPL_OAUTH_KEY
- NYPL_OAUTH_SECRET
- NYPL_OAUTH_URL

See `.env.example` for a sample `.env` file. To specify a different `.env`, use the `--envfile` param (e.g. `--envfile config/qa.env`)

### Schema post

Run the following to upload the content of the given jsonfile to `schemas/[name]`
Expand Down
52 changes: 40 additions & 12 deletions bin/nypl-data-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ const jsdiff = require('diff')
const avsc = require('avsc')
require('colors')

const argv = require('minimist')(process.argv.slice(2))
const argv = require('minimist')(process.argv.slice(2), {
default: {
envfile: '.env'
}
})

const DataApiClient = require('../')
require('dotenv').config()
require('dotenv').config({ path: argv.envfile })

const log_level = argv.log_level || 'error'

Expand All @@ -28,6 +33,20 @@ var doPost = function (path, content) {
})
}

var doPatch = function (path, content) {
if ((typeof content) !== 'object') {
content = JSON.parse(content)
}

client.patch(path, content)
.then((resp) => {
console.log(`Done PATCHing ${path}`)
console.log(`Response: ${JSON.stringify(resp, null, 2)}`)
}).catch((e) => {
console.error('Error: ', e)
})
}

var doGet = function (path) {
if (!path) throw new Error('Path required')

Expand Down Expand Up @@ -62,25 +81,27 @@ var showDiff = function (one, two) {
console.log()
}

function promptToPost (path, content) {
if (!content) throw new Error('Posting requires content to post')
function promptTo (method, path, content, cb) {
if (argv.content) {
content = fs.readFileSync(argv.content, 'utf8')
}
if (!content) throw new Error(`${method}ing requires content to ${method}`)

try {
// Assume all posted bodies are json
console.log('content: ', content)
// Assume all posted/patched bodies are json
content = JSON.parse(content)
} catch (e) {
throw new Error('Content to POST does not appear to be JSON. Only JSON supported currently.')
throw new Error(`Content to ${method} does not appear to be JSON. Only JSON supported currently.`)
}

console.log('POSTING')
console.log(`${method}ing`)
console.log(` To endpoint: ${process.env.NYPL_API_BASE_URL}${path}:`)
console.log(` Using credentials: ${process.env.NYPL_OAUTH_KEY}@${process.env.NYPL_OAUTH_URL}`)
console.log(`${JSON.stringify(content, null, 2)}`)
console.log('Proceed?')
prompt.start()
prompt.get('y/n', (e, result) => {
if (result['y/n'] === 'y') doPost(path, content)
if (result['y/n'] === 'y') cb(path, content)
else console.log('Aborting.')
})
}
Expand All @@ -97,10 +118,11 @@ function schemaPost () {
if (previous && next) {
showDiff(previous, next)
}
console.log('Really upload?')
const path = `schemas/${name}`
console.log(`Really upload to ${process.env.NYPL_API_BASE_URL}${path} ?`)
prompt.start()
prompt.get('y/n', (e, result) => {
if (result['y/n'] === 'y') doPost(`schemas/${name}`, next)
if (result['y/n'] === 'y') doPost(path, next)
else console.log('Aborting.')
})
}
Expand Down Expand Up @@ -140,7 +162,13 @@ const commandHash = {
description: 'Post JSON to an arbitrary endpoint. Will prepare request and confirm before proceeding.',
usage: 'nypl-data-api post [path] [inlinejson]',
examples: [ 'nypl-data-api post recap/checkin-requests \'{ "foo": "bar" }\'' ],
exec: (argv.y ? doPost : promptToPost)
exec: (argv.y ? doPost : (path, content) => promptTo('POST', path, content, doPost))
},
patch: {
description: 'Patch JSON to an arbitrary endpoint. Will prepare request and confirm before proceeding.',
usage: 'nypl-data-api patch [path] [inlinejson]',
examples: [ 'nypl-data-api patch hold-requests \'{ "success": false }\'' ],
exec: (argv.y ? doPatch : (path, content) => promptTo('PATCH', path, content, doPatch))
},
get: {
description: 'Get arbitrary endpoint.',
Expand Down
18 changes: 18 additions & 0 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ class Client {
return this._doHttpMethod('POST', path, options)
}

/**
* PATCH a resource at an api endpoint
*
* @param {string} path - The path to fetch (e.g. 'current-schema/Item')
* @param {Object} body - The partial object to pass with the request
* @param {RequestOptions} options - A hash of options.
*
* @return {Promise} A promise that resolves the result data
*/
patch (path, body, options = {}) {
options = Object.assign({
body
}, options)

log.debug(`patch("${path}", ${JSON.stringify(body)} (${typeof body}), ${JSON.stringify(options)})`)
return this._doHttpMethod('PATCH', path, options)
}

/**
* DELETE an object from an api endpoint
*
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

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

24 changes: 24 additions & 0 deletions test/data/hold-requests%2F1234-patch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"data": {
"id": 1234,
"jobId": "1106896193cb7216d96",
"createdDate": "2021-11-16T10:17:06-05:00",
"updatedDate": "2021-11-16T10:17:12-05:00",
"success": false,
"processed": true,
"patron": "987654321",
"nyplSource": "sierra-nypl",
"requestType": "hold",
"recordType": "i",
"record": "15371503",
"pickupLocation": "",
"deliveryLocation": "NH",
"neededBy": "2022-11-16T00:00:00-05:00",
"numberOfCopies": 1,
"docDeliveryData": null,
"error": null
},
"count": 1,
"statusCode": 200,
"debugInfo": []
}
54 changes: 54 additions & 0 deletions test/patch-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
let client = null

describe('Client PATCH method', function () {
beforeEach(() => {
client = require('./make-test-client')()
})

afterEach(() => {
client = null
})

// This is a common patch that the
// [HoldRequestResultConsumer makes](https://github.com/NYPL/hold-request-result-consumer/blob/e7bd5b04afe25cf65ed2a5ce2de0576973e592cb/src/OAuthClient/HoldRequestClient.php#L123)
// makes when it detects a hold-request has been fulfilled
var holdRequestPatch = {
success: true,
processed: true
}

describe('when config.json=true (default)', function () {
it('should accept a new schema object via PATCH and return an object', function () {
return client.patch('hold-requests/1234', holdRequestPatch)
.then((resp) => {
expect(resp).to.be.a('object')
})
})

it('should fail if supplied body is plaintext', function () {
let call = client.patch('hold-requests/1234', JSON.stringify(holdRequestPatch))
return expect(call).to.be.rejected
})

// A null/empty body should be accepted as valid if options.json===true
it('should succeed if supplied body is empty', function () {
let call = client.patch('hold-requests/1234')
return expect(call).to.be.fulfilled
})
})

describe('when config.json=false', function () {
it('should accept a plaintext body and return plain text', function () {
let call = client.patch('hold-requests/1234', JSON.stringify(holdRequestPatch), { json: false })
return Promise.all([
expect(call).to.be.fulfilled,
expect(call).to.eventually.be.a('string')
])
})

it('should fail if supplied body is not plaintext', function () {
let call = client.patch('hold-requests/1234', holdRequestPatch, { json: false })
return expect(call).to.be.rejected
})
})
})

0 comments on commit 36dcc83

Please sign in to comment.