Skip to content

Commit c0f90ec

Browse files
author
Chris Wiechmann
committed
Initial version of the PostgreSQL data-connector.
1 parent 41835ec commit c0f90ec

35 files changed

+1486
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (https://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
node_modules/
37+
jspm_packages/
38+
39+
# TypeScript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
60+
# next.js build output
61+
.next
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# PostgreSQL Connector
2+
3+
The PostgreSQL data connector is a plugin for API Builder that can connect to your PostgreSQL database instance and interrogate your schema that will automatically provision Models into to your project, and optionally, automatically generate a rich CRUD API to the underlying tables. The Models can be used programmatically, or can be used within the flow editor to interact with your database.
4+
5+
## Minimum requirements
6+
7+
### Supported versions
8+
* PostgreSQL 9.6
9+
* Other versions might be supported as well, please create an issue if encounter any.
10+
11+
### Memory
12+
* ~7 MB
13+
14+
### Disk space
15+
* ~10 MB
16+
17+
### Supported features
18+
* Automatic generation of Models from SQL tables
19+
* Automatic generation of API for Models
20+
* Full CRUD operations on tables via Models
21+
* Connection pooling
22+
23+
## Installation
24+
25+
```bash
26+
npm install --no-optional @axway/api-builder-plugin-dc-postgres
27+
```
28+
29+
A configuration file is generated for you and placed into the conf directory of your API Builder project. The configuration for your PostgreSQL is expected to be in environment variables or in the conf/.env file during development.
30+
31+
## Configuration
32+
33+
Once the plugin is installed, the configuration file is located `<project>/conf/postgres.default.js`.
34+
35+
| Option name | Type | Description |
36+
| ----------- | ---- | ----------- |
37+
| connector | string | Must be: `@axway/api-builder-plugin-dc-postgres` |
38+
| connectionPooling | boolean | boolean Enables connection pooling for better performance and scalability. |
39+
| connectionLimit | number | Number of simultaneous connections when connectionPooling is enabled. |
40+
| host | string | The database host. |
41+
| port | number | The database post. |
42+
| database | string | The database instance name. |
43+
| scheme | string | The schema within your database to use |
44+
| user | string | The user with which to connect to the database. |
45+
| password | string | The user's password with which to connect to the database. |
46+
| generateModelsFromSchema | boolean | If enabled, API Builder will automatically interrogate the database and auto-generate Models from SQL tables. |
47+
| modelAutogen | boolean | If enabled, API Builder will automatically generate a full and rich CRUD API from the generated Models. |
48+
49+
## Usage
50+
51+
After you configure the connector, you can start up your API Builder project and visit the console (normally found under http://localhost:8080/console). Your connector will be listed under the [Connectors](http://localhost:8080/console/project/connectors) section of the console.
52+
53+
Your database tables will be listed under the [Models](http://localhost:8080/console/project/models) section of the console. You can now click on the gear icon to the right of the table names and generate flow based APIs.
54+
55+
You can also reference the connector in a custom model.
56+
57+
```javascript
58+
const Account = APIBuilder.Model.extend('Account', {
59+
fields: {
60+
Name: { type: String, required: true }
61+
},
62+
connector: 'postgres'
63+
});
64+
```
65+
66+
If you want to map a specific model to a specific table, use metadata. For example, to map the `account` model to the table named `accounts`, set it such as:
67+
68+
```javascript
69+
const Account = APIBuilder.Model.extend('account', {
70+
fields: {
71+
Name: { type: String, required: false, validator: /[a-zA-Z]{3,}/ }
72+
},
73+
connector: 'postgres',
74+
metadata: {
75+
'mysql': {
76+
table: 'accounts'
77+
}
78+
}
79+
});
80+
```
81+
82+
## Known issues and limitations
83+
84+
1. Only supports SQL tables.
85+
1. Does not support views.
86+
1. Does not support stored procedures.
87+
88+
## Changes
89+
90+
#### 1.0.0
91+
- Initial version of the connector
92+
93+
## Contributing
94+
95+
Please read [Contributing.md](https://github.com/Axway-API-Management-Plus/Common/blob/master/Contributing.md) for details on our code of conduct, and the process for submitting pull requests to us.
96+
97+
## Team
98+
99+
![alt text][Axwaylogo] Axway Team
100+
101+
[Axwaylogo]: https://github.com/Axway-API-Management/Common/blob/master/img/AxwayLogoSmall.png "Axway logo"
102+
103+
[1]: https://docs.axway.com/bundle/API_Builder_4x_allOS_en/page/api_builder_flows.html
104+
[2]: https://docs.axway.com/bundle/API_Builder_4x_allOS_en/page/api_builder_getting_started_guide.html
105+
[3]: https://github.com/Axway-API-Builder-Ext/api-builder-extras/issues
106+
[4]: https://docs.axway.com/bundle/API_Builder_4x_allOS_en/page/environmentalization.html
107+
[5]: https://docs.axway.com/bundle/API_Builder_4x_allOS_en/page/project_configuration.html#ProjectConfiguration-Configurationfiles
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Use this file to configure your Postgres data connector
3+
*
4+
* By default, Postgres host, username, password and port are environment variables.
5+
* You may configure them in conf/.env file during development phase.
6+
* Example setting of environment variables manually:
7+
* linux/mac: export POSTGRES_PASSWORD=password
8+
* windows: setx POSTGRES_PASSWORD 'password'
9+
*/
10+
module.exports = {
11+
connectors: {
12+
postgres: {
13+
connector: '@axway/api-builder-plugin-dc-postgres',
14+
connectionPooling: true,
15+
connectionLimit: 10,
16+
host: process.env.POSTGRES_HOST,
17+
port: process.env.POSTGRES_PORT,
18+
database: process.env.POSTGRES_DB,
19+
user: process.env.POSTGRES_USER,
20+
password: process.env.POSTGRES_PASSWORD,
21+
schema: 'public',
22+
23+
// Create models based on your schema that can be used in your API.
24+
generateModelsFromSchema: true,
25+
26+
// Whether or not to generate APIs based on the methods in generated models.
27+
modelAutogen: false
28+
}
29+
}
30+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Welcome to the Postgres connector!
3+
*/
4+
const semver = require('semver');
5+
6+
/**
7+
* Creates the Postgres connector for API Builder.
8+
* @param {object} APIBuilder - API Builder instance
9+
* @returns {object} Connector instance
10+
*/
11+
exports.create = function (APIBuilder) {
12+
if (semver.lt(APIBuilder.Version || '0.0.0', '4.0.0-0')) {
13+
throw new Error('This connector requires at least version 4.0.0 of API Builder.');
14+
}
15+
const Connector = APIBuilder.Connector;
16+
const Capabilities = Connector.Capabilities;
17+
18+
return Connector.extend({
19+
filename: module.filename,
20+
capabilities: [
21+
Capabilities.ConnectsToADataSource,
22+
Capabilities.ValidatesConfiguration,
23+
// Capabilities.ContainsModels,
24+
Capabilities.GeneratesModels,
25+
Capabilities.CanCreate,
26+
Capabilities.CanRetrieve,
27+
Capabilities.CanUpdate,
28+
Capabilities.CanDelete
29+
// Capabilities.AuthenticatesThroughConnector
30+
]
31+
});
32+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const { Pool } = require('pg');
2+
3+
/**
4+
* Connects to your data store; this connection can later be used by your connector's methods.
5+
* @param {function} next - callback
6+
*/
7+
exports.connect = function (next) {
8+
if (this.config.connection_pooling || this.config.connectionPooling) {
9+
this.pool = new Pool(this.config); //pg.createPool(this.config);
10+
this.pool.connect((err, connection) => {
11+
if (err) {
12+
if (err.code === 'ECONNREFUSED' || err.code === 'ENOTFOUND') {
13+
err.message = 'Connecting to your Postgres server failed; either it isn\'t running, or your connection details are invalid.';
14+
}
15+
next(err);
16+
} else {
17+
// We successfully verified that the pool is working;
18+
// release the connection for future use.
19+
connection.release();
20+
next();
21+
}
22+
});
23+
} else {
24+
this.connection = pg.createConnection(this.config);
25+
this.connection.connect(next);
26+
}
27+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Disconnects from your data store.
3+
* @param {function} next - callback
4+
*/
5+
exports.disconnect = function (next) {
6+
const toEnd = this.pool || this.connection;
7+
8+
if (toEnd) {
9+
toEnd.end(() => {
10+
this.pool = this.connection = null;
11+
next();
12+
});
13+
} else {
14+
next();
15+
}
16+
};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const APIBuilder = require('@axway/api-builder-runtime');
2+
3+
/**
4+
* Fetches metadata describing your connector's proper configuration.
5+
* @param {function} next - callback
6+
*/
7+
exports.fetchMetadata = function fetchMetadata (next) {
8+
next(null, {
9+
fields: [
10+
APIBuilder.Metadata.Checkbox({
11+
name: 'connectionPooling',
12+
description: 'Whether or not to use connection pooling',
13+
required: false
14+
}),
15+
APIBuilder.Metadata.NumField({
16+
name: 'connectionLimit',
17+
description: 'If using connectionPooling, the number of connections to allow',
18+
required: false
19+
}),
20+
APIBuilder.Metadata.Text({
21+
name: 'host',
22+
description: 'The host name of your database',
23+
required: true
24+
}),
25+
APIBuilder.Metadata.NumField({
26+
name: 'port',
27+
description: 'The port your database is running on',
28+
default: 3306,
29+
required: false
30+
}),
31+
APIBuilder.Metadata.Text({
32+
name: 'database',
33+
description: 'The name of your database',
34+
default: 'test',
35+
required: true
36+
}),
37+
APIBuilder.Metadata.Text({
38+
name: 'user',
39+
description: 'The username for connecting to your database',
40+
required: true
41+
}),
42+
APIBuilder.Metadata.Text({
43+
name: 'password',
44+
description: 'The password for connecting to your database',
45+
required: false
46+
}),
47+
APIBuilder.Metadata.Checkbox({
48+
name: 'generateModelsFromSchema',
49+
description: 'Whether or not to generate models from your schema',
50+
required: false
51+
}),
52+
APIBuilder.Metadata.Checkbox({
53+
name: 'modelAutogen',
54+
description: 'Whether or not generated models should create their own APIs',
55+
required: false
56+
})
57+
]
58+
});
59+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Creates a new Model or Collection object.
3+
* @param {APIBuilder.Model} Model The model class being updated.
4+
* @param {Object} [values] Attributes to set on the new model(s).
5+
* @param {Function} callback Callback passed an Error object (or null if successful),
6+
* and the new model or collection.
7+
* @throws {Error}
8+
*/
9+
exports.create = function (Model, values, callback) {
10+
const table = this.getTableName(Model);
11+
const payload = Model.instance(values, false).toPayload();
12+
const primaryKeyColumn = this.getPrimaryKeyColumn(Model);
13+
const isAutogenerated = this.getAutogenerated(Model);
14+
const columns = this.fetchColumns(table, payload);
15+
const placeholders = columns.map(this.returnPlaceholder);
16+
const data = Object.values(payload);
17+
let query;
18+
19+
if (!primaryKeyColumn) {
20+
query = `INSERT INTO ${this.escapeKeys([ table ])[0]} (${this.escapeKeys(columns).join(',')}) VALUES (${placeholders.join(',')})`;
21+
} else if (!isAutogenerated) {
22+
data.unshift(values[primaryKeyColumn]);
23+
placeholders.push('?');
24+
/* eslint-disable */
25+
query = 'INSERT INTO ' + this.escapeKeys([ table ])[0] + ' (' + primaryKeyColumn + ',' + this.escapeKeys(columns).join(',') + ') VALUES (' + placeholders.join(',') + ')';
26+
} else {
27+
/* eslint-disable */
28+
query = 'INSERT INTO ' + this.escapeKeys([ table ])[0] + ' (' + this.escapeKeys(columns).join(',') + ') VALUES (' + placeholders.join(',') + ')';
29+
}
30+
31+
this._query(query, data, callback, (result) => {
32+
const instance = Model.instance(values);
33+
const primaryKey = primaryKeyColumn && this.metadata.schema.objects[table][primaryKeyColumn];
34+
if (primaryKey) {
35+
if (isAutogenerated) {
36+
instance.setPrimaryKey(result.insertId);
37+
} else {
38+
instance.setPrimaryKey(values[primaryKeyColumn]);
39+
}
40+
}
41+
callback(null, instance);
42+
});
43+
};

0 commit comments

Comments
 (0)