Skip to content

Commit 9e390ef

Browse files
author
Jamie Peabody
committed
add PDF flow-node
1 parent 49b6a9e commit 9e390ef

File tree

8 files changed

+390
-0
lines changed

8 files changed

+390
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# @axway/api-builder-plugin-fn-pdf
2+
3+
PDF flow-node for [API Builder](https://docs.axway.com/bundle/api-builder/page/docs/index.html) provides functions to generate and parse PDF files.
4+
5+
## Install
6+
7+
Install within an API Builder project using:
8+
9+
```bash
10+
npm install @axway/api-builder-plugin-fn-pdf
11+
```
12+
13+
## Methods
14+
15+
The following sections provide details of the PDF flow-node methods.
16+
17+
### Generate PDF from markdown
18+
19+
Generates PDF from markdown text using https://www.npmjs.com/package/md-to-pdf.
20+
21+
#### Parameters
22+
23+
| Parameter | Type | Description | Configuration selection | Required |
24+
| --- | --- | --- | --- | --- |
25+
| Markdown | string | A github flavored markdown string. | Selector, String | Yes |
26+
27+
#### Outputs
28+
29+
| Output | Type | Description | Save output value as: |
30+
| --- | --- | --- | --- |
31+
| Next | any | The PDF data. | `$.pdf` |
32+
| Error | any | The PDF generation failed. | `$.error` |
33+
34+
### Parse PDF data
35+
36+
Parses a PDF using https://www.npmjs.com/package/pdf-parse.
37+
38+
#### Parameters
39+
40+
| Parameter | Type | Description | Configuration selection | Required |
41+
| --- | --- | --- | --- | --- |
42+
| Data | Buffer | A PDF Buffer. | Selector, Object | Yes |
43+
44+
#### Outputs
45+
46+
| Output | Type | Description | Save output value as: |
47+
| --- | --- | --- | --- |
48+
| Next | any | The parsed PDF. | `$.parsedPdf` |
49+
| Error | any | PDF parsing failed. | `$.error` |
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "@axway/api-builder-plugin-fn-pdf",
3+
"version": "1.0.0",
4+
"description": "API Builder plugin for PDF",
5+
"author": "",
6+
"license": "ISC",
7+
"keywords": [
8+
"amplify",
9+
"api-builder"
10+
],
11+
"engines": {
12+
"node": ">=14.17",
13+
"api-builder": ">=4.5.0"
14+
},
15+
"main": "src/index.js",
16+
"files": [
17+
"src",
18+
"README.md"
19+
],
20+
"dependencies": {
21+
"@axway/api-builder-sdk": "^1.1.13",
22+
"md-to-pdf": "^5.1.0",
23+
"pdf-parse": "^1.1.1"
24+
},
25+
"devDependencies": {
26+
"@axway/api-builder-test-utils": "^1.4.0",
27+
"chai": "^4.1.2",
28+
"mocha": "^9.1.2"
29+
},
30+
"scripts": {
31+
"test": "mocha ./test --recursive -R spec"
32+
}
33+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
const { mdToPdf } = require('md-to-pdf');
2+
const pdf = require('pdf-parse');
3+
4+
async function generatePDFFromMarkdown(params) {
5+
const {
6+
markdown
7+
} = params;
8+
if (!markdown) {
9+
throw new Error('Missing required parameter: markdown');
10+
}
11+
if (typeof markdown !== 'string') {
12+
throw new Error('Invalid parameter: markdown');
13+
}
14+
15+
const doc = {
16+
content: markdown
17+
}
18+
19+
const pdf = await mdToPdf(doc);
20+
return pdf.content;
21+
}
22+
23+
async function parsePDF(params) {
24+
const {
25+
data
26+
} = params;
27+
if (!data) {
28+
throw new Error('Missing required parameter: data');
29+
}
30+
if (!Buffer.isBuffer(data)) {
31+
throw new Error('Invalid parameter: data');
32+
}
33+
const parsed = await pdf(data);
34+
return {
35+
version: parsed.version,
36+
pages: parsed.numpages,
37+
pagesRendered: parsed.numrender,
38+
info: {
39+
version: parsed.info.PDFFormatVersion,
40+
isAcroFormPresent: parsed.info.IsAcroFormPresent,
41+
isXFAPresent: parsed.info.IsXFAPresent,
42+
creator: parsed.info.Creator,
43+
producer: parsed.info.Producer,
44+
created: parsed.info.CreationDate,
45+
modified: parsed.info.ModDate
46+
},
47+
metadata: parsed.metadata,
48+
text: parsed.text
49+
};
50+
}
51+
52+
module.exports = {
53+
generatePDFFromMarkdown,
54+
parsePDF
55+
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
flow-nodes:
2+
pdf:
3+
name: PDF
4+
icon: icon.svg
5+
description: PDF functions.
6+
category: general
7+
methods:
8+
generatePDFFromMarkdown:
9+
name: Generate PDF from markdown
10+
description: Generates a PDF.
11+
parameters:
12+
markdown:
13+
name: Markdown
14+
description: The markdown used to generate the PDF. Uses github flavored markdown.
15+
required: true
16+
initialType: string
17+
schema:
18+
type: string
19+
returns:
20+
name: Next
21+
description: A PDF was successfully generated.
22+
context: $.pdf
23+
schema:
24+
type: object
25+
description: A Buffer.
26+
throws:
27+
name: Error
28+
description: An unexpected error was encountered.
29+
context: $.error
30+
schema:
31+
type: object
32+
properties:
33+
message:
34+
type: string
35+
parsePDF:
36+
name: Parse PDF data
37+
description: Parse a PDF and get general information and text.
38+
parameters:
39+
data:
40+
name: Data
41+
description: The PDF data as a Buffer.
42+
required: true
43+
schema:
44+
type: object
45+
description: A buffer.
46+
returns:
47+
name: Next
48+
description: A PDF was successfully parsed.
49+
context: $.pdfInfo
50+
schema:
51+
type: object
52+
properties:
53+
version:
54+
description: PDF version
55+
type: string
56+
pages:
57+
description: Number of pages
58+
type: number
59+
pagesRendered:
60+
description: Number of pages rendered
61+
type: number
62+
metadata: {}
63+
text:
64+
type: string
65+
info:
66+
type: object
67+
properties:
68+
version:
69+
description: PDF format version
70+
type: string
71+
isAcroFormPresent:
72+
type: boolean
73+
isXFAPresent:
74+
type: boolean
75+
creator:
76+
type: string
77+
producer:
78+
type: string
79+
created:
80+
type: string
81+
modified:
82+
type: string
83+
throws:
84+
name: Error
85+
description: An unexpected error was encountered.
86+
context: $.error
87+
schema:
88+
type: object
89+
properties:
90+
message:
91+
type: string
Lines changed: 23 additions & 0 deletions
Loading
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const path = require('path');
2+
const { SDK } = require('@axway/api-builder-sdk');
3+
const actions = require('./actions');
4+
5+
/**
6+
* Resolves the API Builder plugin.
7+
* @param {object} pluginConfig - The service configuration for this plugin
8+
* from API Builder config.pluginConfig['api-builder-plugin-pluginName']
9+
* @param {string} pluginConfig.proxy - The configured API-builder proxy server
10+
* @param {object} options - Additional options and configuration provided by API Builder
11+
* @param {string} options.appDir - The current directory of the service using the plugin
12+
* @param {string} options.logger - An API Builder logger scoped for this plugin
13+
* @returns {object} An API Builder plugin.
14+
*/
15+
async function getPlugin(pluginConfig, options) {
16+
const sdk = new SDK({ pluginConfig });
17+
sdk.load(path.resolve(__dirname, 'flow-nodes.yml'), actions);
18+
return sdk.getPlugin();
19+
}
20+
21+
module.exports = getPlugin;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
const { expect } = require('chai');
2+
const { MockRuntime } = require('@axway/api-builder-test-utils');
3+
const getPlugin = require('../src');
4+
5+
describe('flow-node pdf', () => {
6+
let plugin;
7+
let flowNode;
8+
beforeEach(async () => {
9+
plugin = await MockRuntime.loadPlugin(getPlugin);
10+
plugin.setOptions({
11+
validateInputs: true,
12+
validateOutputs: true
13+
});
14+
flowNode = plugin.getFlowNode('pdf');
15+
});
16+
17+
describe('#constructor', () => {
18+
it('should define flow-nodes', () => {
19+
expect(plugin).to.be.a('object');
20+
expect(plugin.getFlowNodeIds()).to.deep.equal([ 'pdf' ]);
21+
expect(flowNode).to.be.a('object');
22+
23+
// Ensure the flow-node matches the spec
24+
expect(flowNode.name).to.equal('PDF');
25+
expect(flowNode.description)
26+
.to.equal('PDF functions.');
27+
expect(flowNode.icon).to.be.a('string');
28+
expect(flowNode.getMethods()).to.deep.equal([
29+
'generatePDFFromMarkdown',
30+
'parsePDF'
31+
]);
32+
});
33+
34+
it('should define valid flow-nodes', () => {
35+
// if this is invalid, it will throw and fail
36+
plugin.validate();
37+
});
38+
});
39+
40+
describe('#generatePDFFromMarkdown', () => {
41+
it('should error when missing required parameter', async () => {
42+
// Disable automatic input validation (we want the action to handle this)
43+
plugin.setOptions({ validateInputs: false });
44+
45+
// Invoke method with a null and check error.
46+
const { value, output } = await flowNode.generatePDFFromMarkdown({
47+
markdown: null
48+
});
49+
50+
expect(value).to.be.instanceOf(Error)
51+
.and.to.have.property('message', 'Missing required parameter: markdown');
52+
expect(output).to.equal('error');
53+
});
54+
55+
it('should succeed with valid argument', async () => {
56+
const {
57+
value,
58+
output
59+
} = await flowNode.generatePDFFromMarkdown({
60+
markdown: '# Hello'
61+
});
62+
expect(value).to.match(/^%PDF-1.4/);
63+
expect(output).to.equal('next');
64+
});
65+
});
66+
67+
describe('#parsePDF', () => {
68+
it('should error when missing required parameter', async () => {
69+
// Disable automatic input validation (we want the action to handle this)
70+
plugin.setOptions({ validateInputs: false });
71+
72+
// Invoke method with a null and check error.
73+
const { value, output } = await flowNode.parsePDF({
74+
data: null
75+
});
76+
77+
expect(value).to.be.instanceOf(Error)
78+
.and.to.have.property('message', 'Missing required parameter: data');
79+
expect(output).to.equal('error');
80+
});
81+
82+
it('should succeed with valid argument', async () => {
83+
const {
84+
value: data
85+
} = await flowNode.generatePDFFromMarkdown({
86+
markdown: '# Hello'
87+
});
88+
89+
const {
90+
value,
91+
output
92+
} = await flowNode.parsePDF({
93+
data
94+
});
95+
expect(value).to.deep.include({
96+
pages: 1,
97+
pagesRendered: 1,
98+
version: '1.10.100',
99+
metadata: null,
100+
text: '\n\nHello',
101+
version: '1.10.100'
102+
});
103+
// this is a date
104+
expect(value.info).to.have.property('created');
105+
// this is a date
106+
expect(value.info).to.have.property('modified');
107+
expect(value.info).to.deep.include({
108+
version: '1.4',
109+
isAcroFormPresent: false,
110+
isXFAPresent: false,
111+
creator: 'Chromium',
112+
producer: 'Skia/PDF m100'
113+
})
114+
expect(output).to.equal('next');
115+
});
116+
});
117+
});

0 commit comments

Comments
 (0)