Skip to content

refactor: remove dynamoose #614

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

Merged
merged 7 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ workflows:
branches:
only:
- dev
- fix/template
- fix/schema

- "build-qa":
context: org-global
Expand Down
56 changes: 35 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# Topcoder Challenge API

This microservice provides access and interaction with all sorts of Challenge data.

## Devlopment status

[![Total alerts](https://img.shields.io/lgtm/alerts/g/topcoder-platform/challenge-api.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/topcoder-platform/challenge-api/alerts/)[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/topcoder-platform/challenge-api.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/topcoder-platform/challenge-api/context:javascript)

### Deployment status

Dev: [![CircleCI](https://circleci.com/gh/topcoder-platform/challenge-api/tree/develop.svg?style=svg)](https://circleci.com/gh/topcoder-platform/challenge-api/tree/develop) Prod: [![CircleCI](https://circleci.com/gh/topcoder-platform/challenge-api/tree/master.svg?style=svg)](https://circleci.com/gh/topcoder-platform/challenge-api/tree/master)

## Swagger definition

- [Swagger](https://api.topcoder.com/v5/challenges/docs/)

## Intended use

- Production API

## Related repos
Expand All @@ -23,8 +27,8 @@ Dev: [![CircleCI](https://circleci.com/gh/topcoder-platform/challenge-api/tree/d
- [Frontend App](https://github.com/topcoder-platform/challenge-engine-ui)

## Prerequisites

- [NodeJS](https://nodejs.org/en/) (v10)
- [DynamoDB](https://aws.amazon.com/dynamodb/)
- [AWS S3](https://aws.amazon.com/s3/)
- [Elasticsearch v6](https://www.elastic.co/)
- [Docker](https://www.docker.com/)
Expand Down Expand Up @@ -74,6 +78,7 @@ The following parameters can be set in config files or in env variables:
You can find sample `.env` files inside the `/docs` directory.

## Available commands

1. Drop/delete tables: `npm run drop-tables`
2. Creating tables: `npm run create-tables`
3. Seed/Insert data to tables: `npm run seed-tables`
Expand All @@ -87,11 +92,12 @@ You can find sample `.env` files inside the `/docs` directory.
11. Initialize the local environments: `npm run local:init`
12. Reset the local environments: `npm run local:reset`


### Notes

- The seed data are located in `src/scripts/seed`

## Local Deployment

0. Make sure to use Node v10+ by command `node -v`. We recommend using [NVM](https://github.com/nvm-sh/nvm) to quickly switch to the right version:

```bash
Expand All @@ -104,31 +110,33 @@ You can find sample `.env` files inside the `/docs` directory.
yarn install
```

2. ⚙ Local config
In the `challenge-api` root directory create `.env` file with the next environment variables. Values for **Auth0 config** should be shared with you on the forum.<br>
```bash
# Auth0 config
AUTH0_URL=
AUTH0_PROXY_SERVER_URL=
AUTH0_AUDIENCE=
AUTH0_CLIENT_ID=
AUTH0_CLIENT_SECRET=
2. ⚙ Local config
In the `challenge-api` root directory create `.env` file with the next environment variables. Values for **Auth0 config** should be shared with you on the forum.<br>

# Locally deployed services (via docker-compose)
IS_LOCAL_DB=true
DYNAMODB_URL=http://localhost:8000
```
```bash
# Auth0 config
AUTH0_URL=
AUTH0_PROXY_SERVER_URL=
AUTH0_AUDIENCE=
AUTH0_CLIENT_ID=
AUTH0_CLIENT_SECRET=

# Locally deployed services (via docker-compose)
IS_LOCAL_DB=true
DYNAMODB_URL=http://localhost:8000
```

- Values from this file would be automatically used by many `npm` commands.
- ⚠️ Never commit this file or its copy to the repository!
- Values from this file would be automatically used by many `npm` commands.
- ⚠️ Never commit this file or its copy to the repository!

3. 🚢 Start docker-compose with services which are required to start Topcoder Challenges API locally

```bash
npm run services:up
```

4. ♻ Update following two parts:

- https://github.com/topcoder-platform/challenge-api/blob/develop/src/models/Challenge.js#L116
`throughput: 'ON_DEMAND',` should be updated to `throughput:{ read: 4, write: 2 },`
- https://github.com/topcoder-platform/challenge-api/blob/develop/config/default.js#L27-L28
Expand All @@ -147,15 +155,17 @@ You can find sample `.env` files inside the `/docs` directory.
```

This command will do 3 things:
- create Elasticsearch indexes (drop if exists)
- Initialize the database by cleaning all the records.
- Import the data to the local database and index it to ElasticSearch

- create Elasticsearch indexes (drop if exists)
- Initialize the database by cleaning all the records.
- Import the data to the local database and index it to ElasticSearch

7. 🚀 Start Topcoder Challenge API

```bash
npm start
```

The Topcoder Challenge API will be served on `http://localhost:3000`

## Production deployment
Expand All @@ -180,6 +190,7 @@ The following test parameters can be set in config file or in env variables:
- S3_ENDPOINT: endpoint of AWS S3 API, for unit and e2e test only; default to `localhost:9000`

### Prepare

- Start Local services in docker.
- Create DynamoDB tables.
- Initialize ES index.
Expand All @@ -188,6 +199,7 @@ The following test parameters can be set in config file or in env variables:
Seeding db data is not needed.

### Running unit tests

To run unit tests alone

```bash
Expand All @@ -201,6 +213,7 @@ npm run test:cov
```

### Running integration tests

To run integration tests alone

```bash
Expand All @@ -214,6 +227,7 @@ npm run e2e:cov
```

## Verification

Refer to the verification document `Verification.md`

## Notes
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
"dependencies": {
"@grpc/grpc-js": "^1.8.12",
"@opensearch-project/opensearch": "^2.2.0",
"@topcoder-framework/domain-challenge": "^0.10.13",
"@topcoder-framework/lib-common": "^0.10.13",
"@topcoder-framework/domain-challenge": "^0.10.14",
"@topcoder-framework/lib-common": "^0.10.14",
"aws-sdk": "^2.1145.0",
"axios": "^0.19.0",
"axios-retry": "^3.4.0",
Expand All @@ -53,7 +53,6 @@
"cors": "^2.7.1",
"deep-equal": "^2.2.0",
"dotenv": "^8.2.0",
"dynamoose": "^1.11.1",
"elasticsearch": "^16.7.3",
"express": "^4.15.4",
"express-fileupload": "^1.1.6",
Expand Down
2 changes: 1 addition & 1 deletion src/common/challenge-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ class ChallengeHelper {
}
});
});
if (overview && overview.totalPrizesInCents) {
if (overview && !_.isUndefined(overview.totalPrizesInCents)) {
overview.totalPrizes = overview.totalPrizesInCents / 100;
delete overview.totalPrizesInCents;
}
Expand Down
158 changes: 0 additions & 158 deletions src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const Joi = require("joi");
const _ = require("lodash");
const querystring = require("querystring");
const constants = require("../../app-constants");
const models = require("../models");
const errors = require("./errors");
const util = require("util");
const AWS = require("aws-sdk");
Expand Down Expand Up @@ -184,155 +183,6 @@ function checkIfExists(source, term) {
return false;
}

/**
* Get Data by model id
* @param {String} modelName The dynamoose model name
* @param {String} id The id value
* @returns {Promise<void>}
*/
async function getById(modelName, id) {
return new Promise((resolve, reject) => {
models[modelName]
.query("id")
.eq(id)
.exec((err, result) => {
if (err) {
return reject(err);
}
if (result.length > 0) {
return resolve(result[0]);
} else {
return reject(new errors.NotFoundError(`${modelName} with id: ${id} doesn't exist`));
}
});
});
}

/**
* Get Data by model ids
* @param {String} modelName The dynamoose model name
* @param {Array} ids The ids
* @returns {Promise<Array>} the found entities
*/
async function getByIds(modelName, ids) {
const entities = [];
const theIds = ids || [];
for (const id of theIds) {
entities.push(await getById(modelName, id));
}
return entities;
}

/**
* Validate the data to ensure no duplication
* @param {Object} modelName The dynamoose model name
* @param {String} name The attribute name of dynamoose model
* @param {String} value The attribute value to be validated
* @returns {Promise<void>}
*/
async function validateDuplicate(modelName, name, value) {
const list = await scan(modelName);
for (let i = 0; i < list.length; i++) {
if (list[i][name] && String(list[i][name]).toLowerCase() === String(value).toLowerCase()) {
throw new errors.ConflictError(`${modelName} with ${name}: ${value} already exist`);
}
}
}

/**
* Create item in database
* @param {Object} modelName The dynamoose model name
* @param {Object} data The create data object
* @returns {Promise<void>}
*/
async function create(modelName, data) {
return new Promise((resolve, reject) => {
const dbItem = new models[modelName](data);
dbItem.save((err) => {
if (err) {
return reject(err);
} else {
return resolve(dbItem);
}
});
});
}

/**
* Update item in database
* @param {Object} dbItem The Dynamo database item
* @param {Object} data The updated data object
* @returns {Promise<void>}
*/
async function update(dbItem, data) {
Object.keys(data).forEach((key) => {
dbItem[key] = data[key];
});
return new Promise((resolve, reject) => {
dbItem.save((err) => {
if (err) {
return reject(err);
} else {
return resolve(dbItem);
}
});
});
}

/**
* Get data collection by scan parameters
* @param {Object} modelName The dynamoose model name
* @param {Object} scanParams The scan parameters object
* @returns {Promise<void>}
*/
async function scan(modelName, scanParams) {
return new Promise((resolve, reject) => {
models[modelName].scan(scanParams).exec((err, result) => {
if (err) {
return reject(err);
} else {
return resolve(result.count === 0 ? [] : result);
}
});
});
}

/**
* Get all data collection (avoid default page limit of DynamoDB) by scan parameters
* @param {Object} modelName The dynamoose model name
* @param {Object} scanParams The scan parameters object
* @returns {Array}
*/
async function scanAll(modelName, scanParams) {
let results = await models[modelName].scan(scanParams).exec();
let lastKey = results.lastKey;
while (!_.isUndefined(results.lastKey)) {
const newResult = await models[modelName].scan(scanParams).startAt(lastKey).exec();
results = [...results, ...newResult];
lastKey = newResult.lastKey;
}
return results;
}

/**
* Test whether the given value is partially match the filter.
* @param {String} filter the filter
* @param {String} value the value to test
* @returns {Boolean} the match result
*/
function partialMatch(filter, value) {
if (filter) {
if (value) {
const filtered = xss(filter);
return _.toLower(value).includes(_.toLower(filtered));
} else {
return false;
}
} else {
return true;
}
}

/**
* Download file from S3
* @param {String} bucket the bucket name
Expand Down Expand Up @@ -1322,14 +1172,6 @@ module.exports = {
setResHeaders,
checkIfExists,
toString,
getById,
getByIds,
create,
update,
scan,
scanAll,
validateDuplicate,
partialMatch,
downloadFromFileStack,
downloadFromS3,
deleteFromS3,
Expand Down
Loading