data-api-client
Advanced tools
Comparing version 1.1.0 to 1.2.0
76
index.js
@@ -11,3 +11,3 @@ 'use strict' | ||
* @author Jeremy Daly <jeremy@jeremydaly.com> | ||
* @version 1.1.0 | ||
* @version 1.2.0 | ||
* @license MIT | ||
@@ -108,15 +108,17 @@ */ | ||
// Normize parameters so that they are all in standard format | ||
const normalizeParams = params => params.reduce((acc,p) => | ||
const normalizeParams = params => params.reduce((acc, p) => | ||
Array.isArray(p) ? acc.concat([normalizeParams(p)]) | ||
: Object.keys(p).length === 2 && p.name && p.value ? acc.concat(p) | ||
: acc.concat(splitParams(p)) | ||
,[]) // end reduce | ||
: ( | ||
(Object.keys(p).length === 2 && p.name && p.value !== 'undefined') || | ||
(Object.keys(p).length === 3 && p.name && p.value !== 'undefined' && p.cast) | ||
) ? acc.concat(p) | ||
: acc.concat(splitParams(p)) | ||
, []) // end reduce | ||
// Prepare parameters | ||
const processParams = (sql,sqlParams,params,formatOptions,row=0) => { | ||
const processParams = (engine,sql,sqlParams,params,formatOptions,row=0) => { | ||
return { | ||
processedParams: params.reduce((acc,p) => { | ||
if (Array.isArray(p)) { | ||
let result = processParams(sql,sqlParams,p,formatOptions,row) | ||
const result = processParams(engine,sql,sqlParams,p,formatOptions,row) | ||
if (row === 0) { sql = result.escapedSql; row++ } | ||
@@ -126,6 +128,15 @@ return acc.concat([result.processedParams]) | ||
if (sqlParams[p.name].type === 'n_ph') { | ||
if (p.cast) { | ||
const regex = new RegExp(':' + p.name + '\\b', 'g') | ||
sql = sql.replace( | ||
regex, | ||
engine === 'pg' | ||
? `:${p.name}::${p.cast}` | ||
: `CAST(:${p.name} AS ${p.cast})` | ||
) | ||
} | ||
acc.push(formatParam(p.name,p.value,formatOptions)) | ||
} else if (row === 0) { | ||
let regex = new RegExp('::' + p.name + '\\b','g') | ||
sql = sql.replace(regex,sqlString.escapeId(p.value)) | ||
const regex = new RegExp('::' + p.name + '\\b', 'g') | ||
sql = sql.replace(regex, sqlString.escapeId(p.value)) | ||
} | ||
@@ -190,3 +201,3 @@ return acc | ||
isDate(val) ? 'TIMESTAMP' : undefined | ||
const isDate = val => | ||
@@ -263,3 +274,3 @@ val instanceof Date | ||
// object with named column labels | ||
const formatRecords = (recs,columns,hydrate,formatOptions) => { | ||
const formatRecords = (recs,columns,hydrate,formatOptions) => { | ||
@@ -314,4 +325,4 @@ // Create map for efficient value parsing | ||
const formatRecordValue = (value,typeName,formatOptions) => formatOptions && formatOptions.deserializeDate && | ||
['DATE', 'DATETIME', 'TIMESTAMP', 'TIMESTAMP WITH TIME ZONE'].includes(typeName) | ||
? formatFromTimeStamp(value,(formatOptions && formatOptions.treatAsLocalDate) || typeName === 'TIMESTAMP WITH TIME ZONE') | ||
['DATE', 'DATETIME', 'TIMESTAMP', 'TIMESTAMP WITH TIME ZONE'].includes(typeName) | ||
? formatFromTimeStamp(value,(formatOptions && formatOptions.treatAsLocalDate) || typeName === 'TIMESTAMP WITH TIME ZONE') | ||
: value | ||
@@ -338,3 +349,2 @@ | ||
const query = async function(config,..._args) { | ||
// Flatten array if nested arrays (fixes #30) | ||
@@ -357,7 +367,7 @@ const args = Array.isArray(_args[0]) ? flatten(_args) : _args | ||
// Process parameters and escape necessary SQL | ||
const { processedParams,escapedSql } = processParams(sql,sqlParams,parameters,formatOptions) | ||
const { processedParams,escapedSql } = processParams(config.engine,sql,sqlParams,parameters,formatOptions) | ||
// Determine if this is a batch request | ||
const isBatch = processedParams.length > 0 | ||
&& Array.isArray(processedParams[0]) ? true : false | ||
&& Array.isArray(processedParams[0]) | ||
@@ -391,3 +401,3 @@ // Create/format the parameters | ||
hydrateColumnNames, | ||
args[0].includeResultMetadata === true ? true : false, | ||
args[0].includeResultMetadata === true, | ||
formatOptions | ||
@@ -489,3 +499,23 @@ ) | ||
// Export main function | ||
module.exports = (params) => { | ||
/** | ||
* Create a Data API client instance | ||
* @param {object} params | ||
* @param {'mysql'|'pg'} [params.engine=mysql] The type of database (MySQL or Postgres) | ||
* @param {string} params.resourceArn The ARN of your Aurora Serverless Cluster | ||
* @param {string} params.secretArn The ARN of the secret associated with your | ||
* database credentials | ||
* @param {string} [params.database] The name of the database | ||
* @param {boolean} [params.hydrateColumnNames=true] Return objects with column | ||
* names as keys | ||
* @param {object} [params.options={}] Configuration object passed directly | ||
* into RDSDataService | ||
* @param {object} [params.formatOptions] Date-related formatting options | ||
* @param {boolean} [params.formatOptions.deserializeDate=false] | ||
* @param {boolean} [params.formatOptions.treatAsLocalDate=false] | ||
* @param {boolean} [params.keepAlive] DEPRECATED | ||
* @param {boolean} [params.sslEnabled=true] DEPRECATED | ||
* @param {string} [params.region] DEPRECATED | ||
* | ||
*/ | ||
const init = params => { | ||
@@ -509,2 +539,6 @@ // Set the options for the RDSDataService | ||
const config = { | ||
// Require engine | ||
engine: typeof params.engine === 'string' ? | ||
params.engine | ||
: 'mysql', | ||
@@ -549,3 +583,3 @@ // Require secretArn | ||
} // end config | ||
} // end config | ||
@@ -583,1 +617,3 @@ // Return public methods | ||
} // end exports | ||
module.exports = init |
{ | ||
"name": "data-api-client", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "A lightweight wrapper that simplifies working with the Amazon Aurora Serverless Data API", | ||
@@ -28,3 +28,3 @@ "main": "index.js", | ||
"devDependencies": { | ||
"aws-sdk": "^2.713.0", | ||
"aws-sdk": "^2.811.0", | ||
"eslint": "^6.8.0", | ||
@@ -35,9 +35,7 @@ "jest": "^25.5.4", | ||
"dependencies": { | ||
"sqlstring": "^2.3.1" | ||
"sqlstring": "^2.3.2" | ||
}, | ||
"files": [ | ||
"LICENSE", | ||
"README.md", | ||
"index.js" | ||
] | ||
} |
@@ -27,5 +27,9 @@ ![Aurora Serverless Data API Client](https://user-images.githubusercontent.com/2053544/79285017-44053500-7e8a-11ea-8515-998ccf9c2d2e.png) | ||
let result = await data.query(`SELECT * FROM myTable`) | ||
// [ { id: 1, name: 'Alice', age: null }, | ||
// { id: 2, name: 'Mike', age: 52 }, | ||
// { id: 3, name: 'Carol', age: 50 } ] | ||
// { | ||
// records: [ | ||
// { id: 1, name: 'Alice', age: null }, | ||
// { id: 2, name: 'Mike', age: 52 }, | ||
// { id: 3, name: 'Carol', age: 50 } | ||
// ] | ||
// } | ||
@@ -37,3 +41,3 @@ // SELECT with named parameters | ||
) | ||
// [ { id: 2, name: 'Mike', age: 52 } ] | ||
// { records: [ { id: 2, name: 'Mike', age: 52 } ] } | ||
@@ -138,2 +142,3 @@ // INSERT with named parameters | ||
| database | `string` | *Optional* default database to use with queries. Can be overridden when querying. | | | ||
| engine | `mysql` or `pg` | The type of database engine you're connecting to (MySQL or Postgres). | `mysql` | | ||
| hydrateColumnNames | `boolean` | When `true`, results will be returned as objects with column names as keys. If `false`, results will be returned as an array of values. | `true` | | ||
@@ -238,2 +243,31 @@ | ~~keepAlive~~ (deprecated) | `boolean` | See [Connection Reuse](#connection-reuse) below. | | | ||
### Type-Casting | ||
The Aurora Data API can sometimes give you trouble with certain data types, such as uuid, unless you explicitly cast them. While you can certainly do this manually in your SQL string, the Data API Client offers a really easy way to handle this for you. | ||
```javascript | ||
const result = await data.query( | ||
'INSERT INTO users(id, email, full_name, metadata) VALUES(:id, :email, :fullName, :metadata)', | ||
[ | ||
{ | ||
name: 'id', | ||
value: newUserId, | ||
cast: 'uuid' | ||
}, | ||
{ | ||
name: 'email', | ||
value: email | ||
}, | ||
{ | ||
name: 'fullName', | ||
value: fullName | ||
}, | ||
{ | ||
name: 'metadata', | ||
value: JSON.stringify(userMetadata), | ||
cast: 'jsonb' | ||
} | ||
] | ||
) | ||
``` | ||
### Batch Queries | ||
@@ -381,3 +415,3 @@ The `RDSDataService` Class provides a `batchExecuteStatement` method that allows you to execute a prepared statement multiple times using different parameter sets. This is only allowed for `INSERT`, `UPDATE` and `DELETE` queries, but is much more efficient than issuing multiple `executeStatement` calls. The Data API Client handles the switching for you based on *how* you send in your parameters. | ||
In order to use the Data API, your execution environment requires several IAM permissions. Below are the minimum permissions required. | ||
In order to use the Data API, your execution environment requires several IAM permissions. Below are the minimum permissions required. **Please Note:** The `Resource: "*"` permission for `rds-data` is recommended by AWS (see [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/list_amazonrdsdataapi.html#amazonrdsdataapi-resources-for-iam-policies)) because Amazon RDS Data API does not support specifying a resource ARN. The credentials specified in Secrets Manager can be used to restrict access to specific databases. | ||
@@ -395,3 +429,3 @@ **YAML:** | ||
- "rds-data:CommitTransaction" | ||
Resource: "arn:aws:rds:{REGION}:{ACCOUNT-ID}:cluster:{YOUR-CLUSTER-NAME}" | ||
Resource: "*" | ||
- Effect: "Allow" | ||
@@ -416,3 +450,3 @@ Action: | ||
], | ||
"Resource": "arn:aws:rds:{REGION}:{ACCOUNT-ID}:cluster:{YOUR-CLUSTER-NAME}" | ||
"Resource": "*" | ||
}, | ||
@@ -427,3 +461,8 @@ { | ||
## Sponsors | ||
[![New Relic](https://user-images.githubusercontent.com/2053544/96728664-55238700-1382-11eb-93cb-82fe7cb5e043.png)](https://ad.doubleclick.net/ddm/trackclk/N1116303.3950900PODSEARCH.COM/B24770737.285235234;dc_trk_aid=479074825;dc_trk_cid=139488579;dc_lat=;dc_rdid=;tag_for_child_directed_treatment=;tfua=;gdpr=${GDPR};gdpr_consent=${GDPR_CONSENT_755}) | ||
<IMG SRC="https://ad.doubleclick.net/ddm/trackimp/N1116303.3950900PODSEARCH.COM/B24770737.285235234;dc_trk_aid=479074825;dc_trk_cid=139488579;ord=[timestamp];dc_lat=;dc_rdid=;tag_for_child_directed_treatment=;tfua=;gdpr=${GDPR};gdpr_consent=${GDPR_CONSENT_755}?" BORDER="0" HEIGHT="1" WIDTH="1" ALT="Advertisement"> | ||
## Contributions | ||
Contributions, ideas and bug reports are welcome and greatly appreciated. Please add [issues](https://github.com/jeremydaly/data-api-client/issues) for suggestions and bug reports or create a pull request. You can also contact me on Twitter: [@jeremy_daly](https://twitter.com/jeremy_daly). |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
48283
510
461
Updatedsqlstring@^2.3.2