testing dynamodb functionality with jest - jestjs

I have a file in my typescript/node project that contains a function to retrieve an item from a dynamodb table as follows:
import { DynamoDBClient, GetItemCommand } from "#aws-sdk/client-dynamodb";
import { companyInterface } from './companyInterface';
import { marshall, unmarshall } from '#aws-sdk/util-dynamodb'
const client = new DynamoDBClient({ region: `${process.env.AWS_REGION}` });
export const retrieveItem = async (id: string): Promise<companyInterface> => {
const cmd = new GetItemCommand({
ConsistentRead: true,
Key: marshall({
id,
}),
TableName: 'testTable',
});
const retrievedItem = await client.send(cmd);
return unmarshall(retrievedItem.Item) as companyInterface;
};
I wish to write a unit test (the project uses jest) that can test whether this retrieveItem function will return items in a specific form (the companyInterface). The documentation at https://jestjs.io/docs/dynamodb says I have to include the following code to connect to a test local database:
const {DocumentClient} = require('aws-sdk/clients/dynamodb');
const isTest = process.env.JEST_WORKER_ID;
const config = {
convertEmptyValues: true,
...(isTest && {
endpoint: 'localhost:8000',
sslEnabled: false,
region: 'local-env',
}),
};
const ddb = new DocumentClient(config);
However, this documentation doesn't seem to lead to information on how to use my custom functions (specifically the retrieveItem), since they are passing the config object to a new DocumentClient. Does anyone know how to specifically test my function with jest, such that I can test whether my function returns items with a specific form/having certain properties?

Related

KuCoin API - TypeError: request.charAt is not a function

I'm trying to make a request to the KuCoin API to query the balance. I'm using the NodeJS API found here but I keep getting the error whenever I execute the code.
And here's the code snippet
data().then(api => {
const apiKey = api.api_key;
const apiSecretKey = api.api_secret;
const contactId = api.contact_id;
const exchange = api.exchange;
const passphrase = 'Passphrase';
/** Init Configure */
const config =
{
key: apiKey, // KC-API-KEY
secret: apiSecretKey, // API-Secret
passphrase: passphrase, // KC-API-PASSPHRASE
environment: "live"
}
API.init(require(config));
if (apiKey && exchange === "KuCoin-Futures") {
console.log("KuCoin Balance")
async function getBalance() {
try {
let r = await API.getAccountOverview()
console.log(r.data)
} catch(err) {
console.log(err)
}
}
return getBalance()
}
});
I the console log I get the following error
TypeError: request.charAt is not a function
at Function.Module._resolveLookupPaths (internal/modules/cjs/loader.js:617:15)
Does anyone know how I can fix this??
There are couple of things which look weird in the code snippet you provided, but the sample code from the kucoin-node-api library you linked should work perfectly fine. In case you are using that one, try this snippet which should show your account info:
const api = require('kucoin-node-api');
const config = {
apiKey: 'YOUR_KUCOIN_API_KEY',
secretKey: 'YOUR_KUCOIN_API_SECRET',
passphrase: 'YOUR_KUCOIN_API_PASSPHRASE',
environment: 'live'
};
api.init(config);
api.getAccounts().then((r) => {
console.log(r.data);
}).catch((e) => {
console.log(e);
});
In case you're using a different library, kucoin-node-sdk maybe (judging by your code snippet), then try to configure it correctly:
config.js file:
module.exports = {
baseUrl: 'https://api.kucoin.com',
apiAuth: {
key: 'YOUR_KUCOIN_API_KEY',
secret: 'YOUR_KUCOIN_API_SECRET',
passphrase: 'YOUR_KUCOIN_API_PASSPHRASE'
},
authVersion: 2
}
and your main.js (or whatever the name is):
const API = require('kucoin-node-sdk');
API.init(require('./config'));
const main = async () => {
const getTimestampRl = await API.rest.Others.getTimestamp();
console.log(getTimestampRl.data);
};
main();
The code above will show you KuCoin server timestamp only, but should be enough to keep going.
Good luck with trading!

AWS Transcribe client does not provide an export named 'transcribeClient'

I'm trying to integrate AWS Transcribe in my Node.JS application. AWS S3 and Polly works fine, but AWS Transcribe does not. I'm using the example code of AWS.
When I want to start a transcribe job by the AWS example code I receive the following error: The requested module './libs/transcribeClient.js' does not provide an export named 'transcribeClient'
That was also the only file where I received the error that required is not defined. I wonder why it only happens with AWS transcribe but not with the other services as well? I'm also able to start a transcribe job via the AWS CLI.
That AWS Transcribe code does not work - transcribeClient.js:
const AWS_BUCKET_NAME="X"
const AWS_REGION="eu-central-1"
const AWS_ACCESS_KEY="XXX"
const AWS_SECRET_KEY="XXX"
// snippet-start:[transcribe.JavaScript.createclientv3]
const { TranscribeClient } = require('#aws-sdk/client-transcribe');
// Create anAmazon EC2 service client object.
const transcribeClient = new TranscribeClient({ AWS_REGION, AWS_ACCESS_KEY, AWS_SECRET_KEY });
module.exports = { transcribeClient };
That AWS Polly code works - pollyClient.js:
const AWS_BUCKET_NAME="X"
const AWS_REGION="eu-central-1"
const AWS_ACCESS_KEY="XXX"
const AWS_SECRET_KEY="XXX"
// snippet-start:[polly.JavaScript.createclientv3]
const { PollyClient } =require( "#aws-sdk/client-polly");
// Create an Amazon S3 service client object.
const pollyClient = new PollyClient({ AWS_REGION, AWS_ACCESS_KEY, AWS_SECRET_KEY});
module.exports = { pollyClient };
I'm looking forward to reading from you! Thanks!
I solved it. Now it's working with my Node.js 12 environment.
package.json
I changed "type": "modules" to "type": "commonjs".
transcribeClient.js needs to look like this:
Here I changed export to module.exports.
const { TranscribeClient } = require("#aws-sdk/client-transcribe");
const transcribeClient = new TranscribeClient({ AWS_REGION, AWS_ACCESS_KEY, AWS_SECRET_KEY});
module.exports = { transcribeClient };
transcribe_create_job.js needs to look like this:
Here I changed the import statement to require.
const { StartTranscriptionJobCommand } = require("#aws-sdk/client-transcribe");
const { transcribeClient } = require("./libs/transcribeClient.js")
// Set the parameters
const params = {
TranscriptionJobName: "test123",
LanguageCode: "en-GB", // For example, 'en-US'
MediaFormat: "webm", // For example, 'wav'
Media: {
MediaFileUri: "https://x.s3.eu-central-1.amazonaws.com/dlpasiddi.webm",
},
};
const run = async () => {
try {
const data = await transcribeClient.send(
new StartTranscriptionJobCommand(params)
);
console.log("Success - put", data);
return data; // For unit tests.
} catch (err) {
console.log("Error", err);
}
};
run();

Is there a way to generate pb files based on the options same as loadSync method uses?

When using dynamic code generation, we can load .proto files using:
const packageDefinition: PackageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
includeDirs: [__dirname],
});
We can set keepCase option to preserve field names, don't change them to camel case. And enums option, so we can use the string represent of enums.
Now, I am using static code generation. Facing above two problems, here is a method implementation of my service:
public async getTopics(call: ServerUnaryCall<GetTopicsRequest>, callback: sendUnaryData<GetTopicsResponse>) {
const obj = call.request.toObject();
const url = `${config.CNODE_API_URL}/topics`;
const params = {
page: obj.page || obj.page + 1,
tab: (getEnumKeyByEnumValue(Tab, obj.tab) || '').toLowerCase(),
mdrender: (getEnumKeyByEnumValue(Mdrender, obj.mdrender) || 'true').toLowerCase(),
limit: obj.limit,
};
try {
const res = await axios.get(url, { params });
const data = res.data;
// console.log(data);
const topicsResponse = new GetTopicsResponse();
data.data.forEach((po, i) => {
const topic = new Topic();
topic.setId(po.id);
topic.setAuthorId(po.author_id);
topic.setTab(Tab[po.tab.toUpperCase() as keyof typeof Tab]);
topic.setContent(po.content);
topic.setTitle(po.title);
topic.setLastReplyAt(po.last_reply_at);
topic.setGood(po.good);
topic.setTop(po.top);
topic.setReplyCount(po.reply_count);
topic.setVisitCount(po.visit_count);
topic.setCreateAt(po.create_at);
const author = new UserBase();
author.setLoginname(po.author.loginname);
author.setAvatarUrl(po.avatar_url);
topic.setAuthor(author);
topicsResponse.addData(topic, i);
});
topicsResponse.setSuccess(data.success);
callback(null, topicsResponse);
} catch (error) {
console.log(error);
const metadata = new Metadata({ idempotentRequest: true });
metadata.set('params', JSON.stringify(params));
metadata.set('url', url);
const ErrGetTopics: ServiceError = { code: status.INTERNAL, name: 'getTopicsError', message: 'call CNode API failed', metadata };
callback(ErrGetTopics, null);
}
}
Here are the problems I am facing:
Can't set default value when using proto3, want to set default value of page argument to 1, NOT 0.
I need to convert enum from number to string represent manually.
The fields from RESTful API response is snake case, but protoc, grpc_tools_node_protoc and grpc_tools_node_protoc_ts plugin generate models which have camel case fields. So I want to keep case.
As you can see, the hydration process is not convenient and boring. I need to call setters to set value for field one by one.

Writing unit tests for services in feathers without using a database

I would like to write some unit tests for feathers services.
I want this test to run completely independent, which means i do not want to use the database.
This is an example snippet of my service which is using sequelize:
src/services/messages/messages.service.js
// Initializes the `messages` service on path `/messages`
const createService = require('feathers-sequelize');
const createModel = require('../../models/messages.model');
const hooks = require('./messages.hooks');
const filters = require('./messages.filter');
module.exports = function (app) {
const app = this;
const Model = createModel(app);
const paginate = app.get('paginate');
const options = {
name: 'messages',
Model,
paginate
};
// Initialize our service with any options it requires
app.use('/messages', createService(options));
// Get our initialized service so that we can register hooks
const service = app.service('messages');
service.hooks(hooks);
if (service.filter) {
service.filter(filters);
}
};
I would maybe try to mock the database with the library sequelize-test-helpers but I am not sure how this would work in combination with feathers.
This is how my current test in typescript for this service looks like:
src/test/services/messages.test.ts
import assert from 'assert';
import { app } from '../../src/app';
describe('\'messages\' service', () => {
before(() => {
// maybe add an entry to the mocked database
});
after(() => {
// maybe delete that entry
});
it('registered the service', () => {
const service = app.service('messages');
assert.ok(service, 'Registered the service');
});
it('returns a single record', async () => {
// get result with id 1 (maybe added item in before-hook)
const res = await service.get(1);
should().exist(res);
res.should.be.a('object');
// more checks...
});
});
The first 'it(...)' was generated by feathers itself and the second 'it(...)' shows the functionality I want the test to have.
But the problem is that I am not sure how to write this test so that the service will not use the original database.
Does anybody of you have an idea how I could write a test for a feathers service without using the actual database?
Thanks in advance!
Set environment to TEST and in config set the database on the test.json . As seen here : https://docs.feathersjs.com/guides/basics/testing.html#test-database-setup

Retrieve AWS ssm parameter in bulk

How can I retrieve parameters from AWS Systems Manager (parameter store) in bulk (or more than one parameter) at a time? Using aws-sdk, following is the Node.js code I have written to retrieve SSM parameter from parameter store:
const ssm = new (require('aws-sdk/clients/ssm'))()
const getSSMKey = async params => {
const {Parameter: {Value: APIKey}} = await ssm.getParameter(params).promise()
return APIKey
}
const [param1, param2, param3] = await Promise.all([
getSSMKey({ Name: '/data/param/PARAM1', WithDecryption: true }),
getSSMKey({ Name: '/data/param/PARAM2', WithDecryption: true }),
getSSMKey({ Name: '/data/param/PARAM3', WithDecryption: true })
])
console.log(param1, param2, param3)
But with this code, I am sending 3 request for getting 3 parameters which is inefficient in case of large number of parameters. Is there any way to retrieve more than one parameters in one request. if ssm.getParameters() is the method to do that then please give an example (particularly parameter to that method). I tried but I receive nothing.
According to the AWS document, GetParameter gets the value for one parameter, whereas GetParameters gets the value for multiple.
Their usages are very similar too. When using GetParameters to get multiple values, pass in multiple names as a list for Names, instead of passing a single name as string for Name.
Code sample, to get parameters named "foo" and "bar", in "us-west-1" region:
const AWS = require('aws-sdk');
AWS.config.update({ region: "us-west-1" });
const SSM = require('aws-sdk/clients/ssm');
const ssm = new SSM()
const query = {
"Names": ["foo", "bar"],
"WithDecryption": true
}
let param = ssm.getParameters(query, (err, data) => {
console.log('error = %o', err);
console.log('raw data = %o', data);
})
At last it worked for me. Following is the code:
const ssmConfig = async () => {
const data = await ssm.getParameters({ Names: ['/data/param/PARAM1', '/data/param/PARAM2', '/bronto/rest//data/param/PARAM3'],
WithDecryption: true }).promise()
const config = {}
for (const i of data.Parameters) {
if (i.Name === '/data/param/PARAM1') {
config.param1 = i.Value
}
if (i.Name === '/data/param/PARAM2') {
config.rest.clientId param2 = i.Value
}
if (i.Name === '/data/param/PARAM3') {
config.param3 = i.Value
}
}
return config
}
This is what I did to retrieve all the parameters from a specific path.
**your SSM function client :**
'use strict';
const SSM = require('aws-sdk/clients/ssm');
let ssmclient;
module.exports.init = () => {
const region = process.env.REGION === undefined ? 'us-east-1' : process.env.REGION ;
ssmclient = new SSM({region: region});
}
module.exports.getParameters = async (path) => {
try {
let params = {
Path: path,
WithDecryption: true
};
let allParameters = [];
let data = await ssmclient.getParametersByPath(params).promise();
allParameters.push.apply(allParameters, data.Parameters);
while(data.NextToken) {
params.NextToken = data.NextToken;
data = await ssmclient.getParametersByPath(params).promise();
allParameters.push.apply(allParameters, data.Parameters);
}
return allParameters;
} catch (err) {
return Promise.reject(err);
}
}
calling this client:
const ssm = require("yourssmclinet");
ssm.init();
// you call only once to retrieve everything which falls under /data/param
const parameters = await getParameters("/data/param");
//from here you can fetch parameters['what ever needed'].
You essentially have two options to get parameters in bulk.
One is the method provided by #user1032613, but the other is to use the built-in function getParametersByPath().
A Lambda code example in node with all three methods can be seen below. Each method can take different params, for instance with the path you can make filters, etc. to get the exact values you need, see the documentation.
'use strict';
const AWS = require('aws-sdk');
const SSM = new AWS.SSM();
exports.handler = async (event) => {
//Example get single item
const singleParam = { Name: 'myParam' };
const getSingleParam = await SSM.getParameter(singleParam).promise();
//Example: Get Multiple values
const multiParams = {
Names: [ 'myParam1', 'myParam2', 'myParam3' ],
WithDecryption: true
};
const getMultiParams = await SSM(multiParams).promise();
//Example: Get all values in a path
const pathParams = { Path: '/myPath/', WithDecryption: true };
const getPathParams = await SSM.getParametersByPath(pathParams).promise();
return 'Success';
};
Remember that you can also use environment variables. For example, you could write singleParam like this:
const singleParam = { Name: process.env.PARAM }
That way you can have code that extracts code from DEV, PROD, etc. depending on the stage.

Resources