Skip to content
This repository was archived by the owner on Mar 12, 2025. It is now read-only.

Commit c008322

Browse files
author
vikasrohit
authored
Merge pull request #1 from topcoder-platform/develop
First prod push
2 parents 14b850e + 6c079bb commit c008322

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+11121
-2
lines changed

.circleci/config.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
version: 2
2+
defaults: &defaults
3+
docker:
4+
- image: circleci/python:2.7.18-stretch-browsers
5+
install_dependency: &install_dependency
6+
name: Installation of build and deployment dependencies.
7+
command: |
8+
sudo apt install jq
9+
sudo pip install awscli --upgrade
10+
sudo pip install docker-compose
11+
install_deploysuite: &install_deploysuite
12+
name: Installation of install_deploysuite.
13+
command: |
14+
git clone --branch master_hostfix_v1 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript
15+
cp ./../buildscript/master_deploy.sh .
16+
cp ./../buildscript/buildenv.sh .
17+
cp ./../buildscript/awsconfiguration.sh .
18+
restore_cache_settings_for_build: &restore_cache_settings_for_build
19+
key: docker-node-modules-{{ checksum "package-lock.json" }}
20+
21+
save_cache_settings: &save_cache_settings
22+
key: docker-node-modules-{{ checksum "package-lock.json" }}
23+
paths:
24+
- node_modules
25+
26+
builddeploy_steps: &builddeploy_steps
27+
- checkout
28+
- setup_remote_docker
29+
- run: *install_dependency
30+
- run: *install_deploysuite
31+
- restore_cache: *restore_cache_settings_for_build
32+
- run: ./build.sh ${APPNAME}
33+
- save_cache: *save_cache_settings
34+
- deploy:
35+
name: Running MasterScript.
36+
command: |
37+
./awsconfiguration.sh $DEPLOY_ENV
38+
source awsenvconf
39+
./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-deployvar
40+
source buildenvvar
41+
./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-global-appvar,${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME}
42+
jobs:
43+
# Build & Deploy against development backend
44+
"build-dev":
45+
<<: *defaults
46+
environment:
47+
DEPLOY_ENV: "DEV"
48+
LOGICAL_ENV: "dev"
49+
APPNAME: "skills-api"
50+
steps: *builddeploy_steps
51+
52+
"build-prod":
53+
<<: *defaults
54+
environment:
55+
DEPLOY_ENV: "PROD"
56+
LOGICAL_ENV: "prod"
57+
APPNAME: "skills-api"
58+
steps: *builddeploy_steps
59+
60+
workflows:
61+
version: 2
62+
build:
63+
jobs:
64+
# Development builds are executed on "develop" branch only.
65+
- "build-dev":
66+
context : org-global
67+
filters:
68+
branches:
69+
only:
70+
- develop
71+
72+
# Production builds are exectuted only on tagged commits to the
73+
# master branch.
74+
- "build-prod":
75+
context : org-global
76+
filters:
77+
branches:
78+
only: master

README.md

100644100755
Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,157 @@
1-
# skills-api
2-
V5 Skills API
1+
# Skills API
2+
3+
* [Prerequisites](#prerequisites)
4+
* [Configuration](#configuration)
5+
* [Local deployment](#local-deployment)
6+
* [Migrations](#migrations)
7+
* [Local Deployment with Docker](#local-deployment-with-docker)
8+
* [NPM Commands](#npm-commands)
9+
* [JWT Authentication](#jwt-authentication)
10+
* [Documentation](#documentation)
11+
12+
## Prerequisites
13+
14+
- node 12.x+
15+
- npm 6.x+
16+
- docker
17+
- elasticsearch 7.7+
18+
- PostgreSQL
19+
20+
## Configuration
21+
22+
Configuration for the application is at `config/default.js` and `config/production.js`. The following parameters can be set in config files or in env variables:
23+
24+
- LOG_LEVEL: the log level
25+
- PORT: the server port
26+
- AUTH_SECRET: TC Authentication secret
27+
- VALID_ISSUERS: valid issuers for TC authentication
28+
- PAGE_SIZE: the default pagination limit
29+
- MAX_PAGE_SIZE: the maximum pagination size
30+
- API_VERSION: the API version
31+
- DB_NAME: the database name
32+
- DB_USERNAME: the database username
33+
- DB_PASSWORD: the database password
34+
- DB_HOST: the database host
35+
- DB_PORT: the database port
36+
- ES_HOST: Elasticsearch host
37+
- ES_REFRESH: Should elastic search refresh. Default is 'true'. Values can be 'true', 'wait_for', 'false'
38+
- ELASTICCLOUD_ID: The elastic cloud id, if your elasticsearch instance is hosted on elastic cloud. DO NOT provide a value for ES_HOST if you are using this
39+
- ELASTICCLOUD_USERNAME: The elastic cloud username for basic authentication. Provide this only if your elasticsearch instance is hosted on elastic cloud
40+
- ELASTICCLOUD_PASSWORD: The elastic cloud password for basic authentication. Provide this only if your elasticsearch instance is hosted on elastic cloud
41+
- ES.DOCUMENTS: Elasticsearch index, type and id mapping for resources.
42+
- SKILL_INDEX: The Elastic search index for skill. Default is `skill`
43+
- SKILL_ENRICH_POLICYNAME: The enrich policy for skill. Default is `skill-policy`
44+
- TAXONOMY_INDEX: The Elastic search index for taxonomy. Default is `taxonomy`
45+
- TAXONOMY_PIPELINE_ID: The pipeline id for enrichment with taxonomy. Default is `taxonomy-pipeline`
46+
- TAXONOMY_ENRICH_POLICYNAME: The enrich policy for taxonomy. Default is `taxonomy-policy`
47+
- MAX_BATCH_SIZE: Restrict number of records in memory during bulk insert (Used by the db to es migration script)
48+
- MAX_BULK_SIZE: The Bulk Indexing Maximum Limits. Default is `100` (Used by the db to es migration script)
49+
50+
51+
## Local deployment
52+
53+
Setup your Postgresql DB and Elasticsearch instance and ensure that they are up and running.
54+
55+
- Follow *Configuration* section to update config values, like database, ES host etc ..
56+
- Goto *skills-api*, run `npm i`
57+
- Create database using `npm run create-db`.
58+
- Run the migrations - `npm run migrations up`. This will create the tables.
59+
- Then run `npm run insert-data` and insert mock data into the database.
60+
- Run `npm run migrate-db-to-es` to sync data with ES.
61+
- Startup server `npm run start`
62+
63+
## Migrations
64+
65+
Migrations are located under the `./scripts/db/` folder. Run `npm run migrations up` and `npm run migrations down` to execute the migrations or remove the earlier ones
66+
67+
## Local Deployment with Docker
68+
69+
- Navigate to the directory `docker-pgsql-es` folder. Rename `sample.env` to `.env` and change any values if required.
70+
- Run `docker-compose up -d` to have docker instances of pgsql and elasticsearch to use with the api
71+
72+
- Create database using `npm run create-db`.
73+
- Run the migrations - `npm run migrations up`. This will create the tables.
74+
- Then run `npm run insert-data` and insert mock data into the database.
75+
- Run `npm run migrate-db-to-es` to sync data with ES.
76+
77+
- Navigate to the directory `docker`
78+
79+
- Rename the file `sample.env` to `.env`
80+
81+
- Set the required DB configurations and ElasticSearch host in the file `.env`
82+
83+
- Once that is done, run the following command
84+
85+
```bash
86+
docker-compose up
87+
```
88+
89+
- When you are running the application for the first time, It will take some time initially to download the image and install the dependencies
90+
91+
## NPM Commands
92+
93+
| Command&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Description |
94+
|--------------------|--|
95+
| `npm run start` | Start app |
96+
| `npm run start:dev` | Start app on any changes (useful during development). |
97+
| `npm run lint` | Check for for lint errors. |
98+
| `npm run lint:fix` | Check for for lint errors and fix error automatically when possible. |
99+
| `npm run create-db` | Create the database |
100+
| `npm run insert-data` | Insert data into the database |
101+
| `npm run migrate-db-to-es` | Migrate data into elastic search from database |
102+
| `npm run delete-data` | Delete the data from the database |
103+
| `npm run migrations up` | Run up migration |
104+
| `npm run migrations down` | Run down migration |
105+
| `npm run generate:doc:permissions` | Generate [permissions.html](docs/permissions.html) |
106+
| `npm run generate:doc:permissions:dev` | Generate [permissions.html](docs/permissions.html) on any changes (useful during development). |
107+
108+
## JWT Authentication
109+
Authentication is handled via Authorization (Bearer) token header field. Token is a JWT token.
110+
111+
Here is a sample user token that is valid for a very long time for a user with administrator role.
112+
113+
```
114+
<provide_in_forums>
115+
116+
# here is the payload data decoded from the token
117+
{
118+
"roles": [
119+
"Topcoder User",
120+
"administrator"
121+
],
122+
"iss": "https://api.topcoder.com",
123+
"handle": "tc-Admin",
124+
"exp": 1685571460,
125+
"userId": "23166768",
126+
"iat": 1585570860,
127+
"email": "[email protected]",
128+
"jti": "0f1ef1d3-2b33-4900-bb43-48f2285f9630"
129+
}
130+
```
131+
132+
and this is a sample M2M token with scopes `all:connect_project`, `all:projects` and `write:projects`.
133+
134+
```
135+
<provided_in_forums>
136+
137+
# here is the payload data decoded from the token
138+
{
139+
"iss": "https://topcoder-dev.auth0.com/",
140+
"sub": "enjw1810eDz3XTwSO2Rn2Y9cQTrspn3B@clients",
141+
"aud": "https://m2m.topcoder-dev.com/",
142+
"iat": 1550906388,
143+
"exp": 2147483648,
144+
"azp": "enjw1810eDz3XTwSO2Rn2Y9cQTrspn3B",
145+
"scope": "all:connect_project all:projects write:projects",
146+
"gty": "client-credentials"
147+
}
148+
```
149+
150+
These tokens have been signed with the secret `CLIENT_SECRET`. This secret should match the `AUTH_SECRET` entry in `config/default.js`. You can modify the payload of these tokens to generate tokens with different roles or different scopes using https://jwt.io
151+
152+
**Note** Please check with `src/constants.js` for all available user roles and M2M scopes.
153+
154+
## Documentation
155+
156+
- [permissions.html](docs/permissions.html) - the list of all permissions in Skills API.
157+
- [swagger.yaml](docs/swagger.yaml) - the Swagger API Definition.

app.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* The application entry point
3+
*/
4+
5+
require('./src/bootstrap')
6+
const config = require('config')
7+
const express = require('express')
8+
const cross = require('cors')
9+
const bodyParser = require('body-parser')
10+
const _ = require('lodash')
11+
const http = require('http')
12+
const swaggerUi = require('swagger-ui-express')
13+
const jsyaml = require('js-yaml')
14+
const fs = require('fs')
15+
const path = require('path')
16+
const logger = require('./src/common/logger')
17+
const errorMiddleware = require('./src/common/error.middleware')
18+
const routes = require('./src/route')
19+
const { permissions, jwtAuthenticator } = require('tc-core-library-js').middleware
20+
const app = express()
21+
const httpServer = http.Server(app)
22+
const models = require('./src/models')
23+
const initPermissions = require('./src/permissions')
24+
25+
app.set('port', config.PORT)
26+
app.use(bodyParser.json())
27+
app.use(bodyParser.urlencoded({ extended: true }))
28+
app.use(cross())
29+
const apiRouter = express.Router({})
30+
31+
// load all routes
32+
_.each(routes, (verbs, url) => {
33+
_.each(verbs, (def, verb) => {
34+
if (!def.method) {
35+
throw new Error(`${verb.toUpperCase()} ${url} method is undefined`)
36+
}
37+
if (def.auth && def.auth !== 'jwt') {
38+
throw new Error(`auth type "${def.auth}" is not supported`)
39+
}
40+
41+
const actions = []
42+
// Authentication
43+
if (def.auth) {
44+
actions.push((req, res, next) => {
45+
jwtAuthenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, res, next)
46+
})
47+
}
48+
// Authorization
49+
if (def.permission) {
50+
actions.push(permissions(def.permission))
51+
}
52+
// main middleware
53+
actions.push(async (req, res, next) => {
54+
try {
55+
await def.method(req, res, next)
56+
} catch (e) {
57+
next(e)
58+
}
59+
})
60+
61+
logger.info(`Endpoint discovered : ${verb.toLocaleUpperCase()} /${config.API_VERSION}${url}`)
62+
apiRouter[verb](`/${config.API_VERSION}${url}`, actions)
63+
})
64+
})
65+
app.use('/', apiRouter)
66+
const spec = fs.readFileSync(path.join(__dirname, 'docs/swagger.yaml'), 'utf8')
67+
const swaggerDoc = jsyaml.safeLoad(spec)
68+
69+
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc))
70+
71+
app.use(errorMiddleware())
72+
app.use('*', (req, res) => {
73+
const pathKey = req.baseUrl.substring(config.API_VERSION.length + 1)
74+
const route = routes[pathKey]
75+
if (route) {
76+
res.status(405).json({ message: 'The requested method is not supported.' })
77+
} else {
78+
res.status(404).json({ message: 'The requested resource cannot found.' })
79+
}
80+
});
81+
82+
(async () => {
83+
await models.init()
84+
initPermissions() // initialize permission policies
85+
httpServer.listen(app.get('port'), () => {
86+
logger.info(`Express server listening on port ${app.get('port')}`)
87+
})
88+
})()

build.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
set -eo pipefail
3+
APP_NAME=$1
4+
UPDATE_CACHE=""
5+
echo "" > docker/api.env
6+
docker build -f docker/Dockerfile -t $APP_NAME:latest .
7+
docker create --name app $APP_NAME:latest
8+
9+
if [ -d node_modules ]
10+
then
11+
mv package-lock.json old-package-lock.json
12+
docker cp app:/usr/src/app/package-lock.json package-lock.json
13+
set +eo pipefail
14+
UPDATE_CACHE=$(cmp package-lock.json old-package-lock.json)
15+
set -eo pipefail
16+
else
17+
UPDATE_CACHE=1
18+
fi
19+
20+
if [ "$UPDATE_CACHE" == 1 ]
21+
then
22+
docker cp app:/usr/src/app/node_modules .
23+
fi

0 commit comments

Comments
 (0)