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 getEtchPacket and downloadDocuments Methods #26

Merged
merged 19 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from 15 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.DS_STORE
*.log
*.zip

node_modules
package-lock.json
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ Creates an Etch Packet and optionally sends it to the first signer.
}
```

##### getEtchPacket(options)

Gets the details of an Etch Packet.
* `options` (Object) - An object with the following structure:
* `variables` (Object) - Requires `eid`
* `eid` (String) - your Etch Packet eid
* `responseQuery` (String) - _optional_ A GraphQL Query compliant query to use for the data desired in the query response. Can be left out to use default.

##### generateEtchSignUrl(options)

Generates an Etch sign URL for an Etch Packet signer. The Etch Packet and its signers must have already been created.
Expand All @@ -139,6 +147,17 @@ Generates an Etch sign URL for an Etch Packet signer. The Etch Packet and its si
* `clientUserId` (String) - your user eid
* `signerEid` (String) - the eid of the Etch Packet signer, found in the response of the `createEtchPacket` instance method

##### downloadDocuments(documentGroupEid)

Returns the PassThrough stream of the document group specified by the documentGroupEid in Zip file format.
* `documentGroupEid` (string) - the eid of the document group to download
* Returns a `Promise` that resolves to an `Object`
* `statusCode` (Number) - the HTTP status code, `200` is success
* `response` (Object) - the Response object resulting from the client's request to the Anvil app
* `data` (object) - a PassThrough Stream object containing the document group's data in Zip file format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should update this to be more in-line with what's now actually going to happen: buffer of stream. Probably worth following the pattern of fillPDF and/or updating both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will update!

* `errors` (Array of Objects) - Will be present if status >= 400. See Errors
* `message` (String)

### Class Methods

##### prepareGraphQLFile(pathOrStreamLikeThing[, options])
Expand Down
28 changes: 28 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ yarn node example/script/create-etch-packet.js <apiKey> <castEid> <filename>
yarn node example/script/create-etch-packet.js WHG3ylq0EE930IR2LZDtgoqgl55M3TwQ 99u7QvvHr8hDQ4BW9GYv ../../../simple-anvil-finovate-non-qualified.pdf
```

## get-etch-packet.js script

Calls the etchPacket Anvil endpoint with the specified Etch packet eid to get the packet details.

Usage example:

```sh
# Gets the details of an Etch Packet, a packet eid must be supplied
yarn node example/script/get-etch-packet.js <apiKey> <etchPacketEid>

# An example
yarn node example/script/get-etch-packet.js WHG3ylq0EE930IR2LZDtgoqgl55M3TwQ QJhbdpK75RHRQcgPz5Fc
```

## generate-etch-sign-url.js script

Calls the generateEtchSignUrl Anvil endpoint with data specified to generate an Etch sign link with the Anvil API. Returns the sign link.
Expand All @@ -55,3 +69,17 @@ yarn node example/script/generate-etch-sign-url.js <apiKey> <clientUserId> <sign
# An example
yarn node example/script/generate-etch-sign-url.js WHG3ylq0EE930IR2LZDtgoqgl55M3TwQ eBim2Vsv2GqCTJxpjTru ZTlbNhxP2lGkNFsNzcus
```

## download-documents.js script

Calls the downloadDocuments Anvil endpoint to download documents with the specified documentGroupEid in Zip file format. Outputs the downloaded Zip file in `example/script/{etchPacketName}.zip`

Usage example:

```sh
# Downloads a Document Group in a Zip file and outputs in the example/script folder
yarn node example/script/download-documents.js <apiKey> <documentGroupEid>

# An example
yarn node example/script/download-documents.js WHG3ylq0EE930IR2LZDtgoqgl55M3TwQ uQiXw4P4DTmXV1eNDmzH
```
42 changes: 42 additions & 0 deletions example/script/download-documents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const fs = require('fs')
const path = require('path')
const Anvil = require('../../src/index')
const argv = require('yargs')
.usage('Usage: $0 apiKey documentGroupEid')
.demandCommand(2).argv

const [apiKey, documentGroupEid] = argv._

async function main () {
const clientOptions = {
apiKey,
}

const client = new Anvil(clientOptions)

const { statusCode, response, data, errors } = await client.downloadDocuments(documentGroupEid)
if (statusCode === 200) {
const contentDisposition = response.headers.get('content-disposition')
const fileTitle = contentDisposition.split('"')[1]
const scriptDir = __dirname
const outputFilePath = path.join(scriptDir, fileTitle)
const writeStream = fs.createWriteStream(outputFilePath, { encoding: null })
await new Promise((resolve, reject) => {
data.pipe(writeStream)
data.on('error', reject)
writeStream.on('finish', resolve)
})
console.log(statusCode, data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We sure we want to write this data to the console? It would be a lot of blah blah blah, no?

And below, too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be a PassThrough stream object, but I agree it's not necessary. I'll take that out.

} else {
console.log(statusCode, JSON.stringify(errors || data, null, 2))
}
}

main()
.then(() => {
process.exit(0)
})
.catch((err) => {
console.log(err.stack || err.message)
process.exit(1)
})
36 changes: 36 additions & 0 deletions example/script/get-etch-packet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const Anvil = require('../../src/index')
const argv = require('yargs')
.usage('Usage: $0 apiKey etchPacketEid')
.demandCommand(2).argv

const [apiKey, etchPacketEid] = argv._

async function main () {
const clientOptions = {
apiKey,
}

const client = new Anvil(clientOptions)

const variables = {
eid: etchPacketEid,
}

const { statusCode, data, errors } = await client.getEtchPacket({ variables })
console.log(
JSON.stringify({
statusCode,
data,
errors,
}, null, 2),
)
}

main()
.then(() => {
process.exit(0)
})
.catch((err) => {
console.log(err.stack || err.message)
process.exit(1)
})
2 changes: 2 additions & 0 deletions src/graphql/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const mutations = require('./mutations')
const queries = require('./queries')

module.exports = {
mutations,
queries,
}
6 changes: 5 additions & 1 deletion src/graphql/mutations/createEtchPacket.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,28 @@ const defaultResponseQuery = `{
id
eid
name
detailsURL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be mindful of what's added here that may be to satisfy your example repo usage vs. what should truly be sensible defaults. If there's something you think might not reasonably belong in a default response query then let's leave it out...and if your app needs it, then it can pass its own custom response query.

I think that these are all fine in that regard, and no need to change them, but let's be mindful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and thanks for pointing that out. I'm taking downloadZipURL out as I don't see it being necessary.

documentGroup {
id
eid
status
files
downloadZipURL
signers {
id
eid
aliasId
routingOrder
name
email
status
signActionType
}
}
}`

module.exports = {
getMutation: (responseQuery = defaultResponseQuery) => `
generateMutation: (responseQuery = defaultResponseQuery) => `
mutation CreateEtchPacket (
$name: String,
$files: [EtchFile!],
Expand Down
2 changes: 1 addition & 1 deletion src/graphql/mutations/generateEtchSignUrl.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
getMutation: () => `
generateMutation: () => `
mutation GenerateEtchSignURL (
$signerEid: String!,
$clientUserId: String!,
Expand Down
35 changes: 35 additions & 0 deletions src/graphql/queries/etchPacket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const defaultResponseQuery = `{
id
eid
name
detailsURL
documentGroup {
id
eid
status
files
downloadZipURL
signers {
id
eid
aliasId
routingOrder
name
email
status
signActionType
}
}
}`

module.exports = {
generateQuery: (responseQuery = defaultResponseQuery) => `
query GetEtchPacket (
$eid: String!,
) {
etchPacket (
eid: $eid,
) ${responseQuery}
}
`,
}
11 changes: 11 additions & 0 deletions src/graphql/queries/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const fs = require('fs')

const IGNORE_FILES = ['index.js']

module.exports = fs.readdirSync(__dirname)
.filter((fileName) => (fileName.endsWith('.js') && !fileName.startsWith('.') && !IGNORE_FILES.includes(fileName)))
.reduce((acc, fileName) => {
const queryName = fileName.slice(0, fileName.length - 3)
acc[queryName] = require(`./${queryName}`)
return acc
}, {})
30 changes: 26 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ const { version, description } = require('../package.json')
const {
mutations: {
createEtchPacket: {
getMutation: getCreateEtchPacketMutation,
generateMutation: generateCreateEtchPacketMutation,
},
generateEtchSignUrl: {
getMutation: getGenerateEtchSignUrlMutation,
generateMutation: generateEtchSignUrlMutation,
},
},
queries: {
etchPacket: {
generateQuery: generateEtchPacketQuery,
},
},
} = require('./graphql')
Expand Down Expand Up @@ -111,7 +116,17 @@ class Anvil {
createEtchPacket ({ variables, responseQuery, mutation }) {
return this.requestGraphQL(
{
query: mutation || getCreateEtchPacketMutation(responseQuery),
query: mutation || generateCreateEtchPacketMutation(responseQuery),
variables,
},
{ dataType: DATA_TYPE_JSON },
)
}

getEtchPacket ({ variables, responseQuery }) {
return this.requestGraphQL(
{
query: generateEtchPacketQuery(responseQuery),
variables,
},
{ dataType: DATA_TYPE_JSON },
Expand All @@ -121,7 +136,7 @@ class Anvil {
async generateEtchSignUrl ({ variables }) {
const { statusCode, data, errors } = await this.requestGraphQL(
{
query: getGenerateEtchSignUrlMutation(),
query: generateEtchSignUrlMutation(),
variables,
},
{ dataType: DATA_TYPE_JSON },
Expand All @@ -134,6 +149,13 @@ class Anvil {
}
}

downloadDocuments (documentGroupEid) {
return this.requestREST(
`/api/document-group/${documentGroupEid}.zip`,
{ method: 'GET' },
{ dataType: DATA_TYPE_STREAM })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why default to Stream here vs Buffer (which is the default for fillPDF? And why not allow it to be overridden to be the other type (Buffer in this case)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defaulted to stream since it was how I used the API in the example repo. But I've just added the option to choose between stream and buffer, following the approach of fillPDF with buffer being the default.

}

async requestGraphQL ({ query, variables = {} }, clientOptions) {
// Some helpful resources on how this came to be:
// https://github.com/jaydenseric/graphql-upload/issues/125#issuecomment-440853538
Expand Down