From 5438fb19df021fc94a82dd808ab076e9fabbac73 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Sat, 16 Mar 2019 17:34:04 +0800 Subject: [PATCH 1/2] winning submission from challenge 30086428 - Topcoder Project Service - Create demo data --- README.md | 4 +- local/seed/index.js | 13 ++ local/seed/projects.json | 178 +++++++++++++++++++++ {migrations => local/seed}/seedMetadata.js | 29 ++-- local/seed/seedProjects.js | 74 +++++++++ package.json | 3 +- 6 files changed, 284 insertions(+), 17 deletions(-) create mode 100644 local/seed/index.js create mode 100644 local/seed/projects.json rename {migrations => local/seed}/seedMetadata.js (80%) create mode 100644 local/seed/seedProjects.js diff --git a/README.md b/README.md index 6d964185..c8356d59 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,9 @@ Microservice to manage CRUD operations for all things Projects. Runs the Project Service using nodemon, so it would be restarted after any of the files is updated. The project service will be served on `http://localhost:8001`. -### Import sample metadata +### Import sample metadata & projects ```bash -CONNECT_USER_TOKEN= node migrations/seedMetadata.js +CONNECT_USER_TOKEN= npm run demo-data ``` This command will create sample metadata entries in the DB (duplicate what is currently in development environment). diff --git a/local/seed/index.js b/local/seed/index.js new file mode 100644 index 00000000..c13edcce --- /dev/null +++ b/local/seed/index.js @@ -0,0 +1,13 @@ +const seedMetadata = require('./seedMetadata'); +const seedProjects = require('./seedProjects'); + +const targetUrl = 'http://localhost:8001/v4/'; +const token = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw'; + +async function seed() { + await seedMetadata(targetUrl, token); + await seedProjects(targetUrl, token); +} + +seed(); diff --git a/local/seed/projects.json b/local/seed/projects.json new file mode 100644 index 00000000..8d4fb65f --- /dev/null +++ b/local/seed/projects.json @@ -0,0 +1,178 @@ +[ + { + "param": { + "name": "Develop app", + "details": { + "utm": { + "code": "R&D" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 1, + "type": "app", + "status": "draft" + } + }, + { + "param": { + "name": "Develop website", + "details": { + "utm": { + "code": "" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 2, + "type": "chatbot", + "status": "in_review" + } + }, + { + "param": { + "name": "Develop website 2", + "details": { + "utm": { + "code": "" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 3, + "type": "website", + "status": "reviewed" + } + }, + { + "param": { + "name": "Develop chatbot", + "details": { + "utm": { + "code": "" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 4, + "type": "chatbot", + "status": "active" + } + }, + { + "param": { + "name": "Develop app 2", + "details": { + "utm": { + "code": "" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 1, + "type": "app", + "status": "completed" + } + }, + { + "param": { + "name": "Develop website 3", + "details": { + "utm": { + "code": "" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 2, + "type": "website", + "status": "paused" + } + }, + { + "param": { + "name": "Develop app 3", + "details": { + "utm": { + "code": "" + }, + "appDefinition": { + "primaryTarget": "phone", + "goal": { + "value": "Nothing" + }, + "users": { + "value": "No one" + }, + "notes": "" + }, + "hideDiscussions": true + }, + "description": "Hello this is a sample description... This requires at least 160 characters. I'm trying to satisfy this condition. But I could n't if I don't type this unnecessary message", + "templateId": 1, + "type": "app", + "status": "cancelled", + "cancelReason": "Test cancel" + } + } +] diff --git a/migrations/seedMetadata.js b/local/seed/seedMetadata.js similarity index 80% rename from migrations/seedMetadata.js rename to local/seed/seedMetadata.js index 790a1e55..5cfef2a3 100644 --- a/migrations/seedMetadata.js +++ b/local/seed/seedMetadata.js @@ -12,18 +12,18 @@ if (!process.env.CONNECT_USER_TOKEN) { const CONNECT_USER_TOKEN = process.env.CONNECT_USER_TOKEN; var url = 'https://api.topcoder-dev.com/v4/projects/metadata'; -var targetUrl = 'http://localhost:8001/v4/'; -var destUrl = targetUrl + 'projects/'; -var destTimelines = targetUrl; - -console.log('Getting metadata from DEV environment...'); - -axios.get(url, { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${CONNECT_USER_TOKEN}` - } -}) + +module.exports = (targetUrl, token) => { + var destUrl = targetUrl + 'projects/'; + var destTimelines = targetUrl; + + console.log('Getting metadata from DEV environment...'); + return axios.get(url, { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${CONNECT_USER_TOKEN}` + } + }) .catch((err) => { const errMessage = _.get(err, 'response.data.result.content.message'); throw errMessage ? new Error('Error during obtaining data from DEV: ' + errMessage) : err @@ -35,7 +35,7 @@ axios.get(url, { var headers = { 'Content-Type': 'application/json', - 'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiYWRtaW5pc3RyYXRvciJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci1kZXYuY29tIiwiaGFuZGxlIjoidGVzdDEiLCJleHAiOjI1NjMwNzY2ODksInVzZXJJZCI6IjQwMDUxMzMzIiwiaWF0IjoxNDYzMDc2MDg5LCJlbWFpbCI6InRlc3RAdG9wY29kZXIuY29tIiwianRpIjoiYjMzYjc3Y2QtYjUyZS00MGZlLTgzN2UtYmViOGUwYWU2YTRhIn0.wKWUe0-SaiFVN-VR_-GwgFlvWaDkSbc8H55ktb9LAVw' + 'Authorization': 'Bearer ' + token } let promises = _(data.result.content.projectTypes).map(pt=>{ @@ -92,7 +92,8 @@ axios.get(url, { )); // handle success - console.log('Done'); + console.log('Done metadata seed'); }).catch(err=>{ console.error(err && err.response ? err.response : err); }); +} diff --git a/local/seed/seedProjects.js b/local/seed/seedProjects.js new file mode 100644 index 00000000..36cc3d79 --- /dev/null +++ b/local/seed/seedProjects.js @@ -0,0 +1,74 @@ +const axios = require('axios'); +const Promise = require('bluebird'); +const _ = require('lodash'); + +const projects = require('./projects.json'); + +/** + * Create projects and update their statuses. + */ +module.exports = (targetUrl, token) => { + let projectPromises; + + const projectsUrl = `${targetUrl}projects`; + const headers = { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }; + + console.log('Creating projects'); + projectPromises = projects.map((project, i) => { + const status = _.get(project, 'param.status'); + const cancelReason = _.get(project, 'param.cancelReason'); + delete project.param.status; + delete project.param.cancelReason; + + return axios + .post(projectsUrl, project, { headers }) + .catch((err) => { + console.log(`Failed to create project ${i}: ${err.message}`); + }) + .then((response) => { + const projectId = _.get(response, 'data.result.content.id'); + + return { + projectId, + status, + cancelReason, + }; + }); + }); + + return Promise.all(projectPromises) + .then((createdProjects) => { + console.log('Updating statuses'); + return Promise.all( + createdProjects.map(({ projectId, status, cancelReason }) => + updateProjectStatus(projectId, { status, cancelReason }, targetUrl, headers).catch((ex) => { + console.log(`Failed to update project status of project with id ${projectId}: ${ex.message}`); + }), + ), + ); + }) + .then(() => console.log('Done project seed.')) + .catch(ex => console.error(ex)); +}; + +function updateProjectStatus(project, updateParams, targetUrl, headers) { + const projectUpdateUrl = `${targetUrl}projects/${project}`; + + // only cancelled status requires cancelReason + if (updateParams.status !== 'cancelled') { + delete updateParams.cancelReason; + } + + return axios.patch( + projectUpdateUrl, + { + param: updateParams, + }, + { + headers, + }, + ); +} diff --git a/package.json b/package.json index d96b1164..c1921857 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "start:dev": "NODE_ENV=development PORT=8001 nodemon -w src --exec \"babel-node src --presets es2015\" | ./node_modules/.bin/bunyan", "test": "NODE_ENV=test npm run lint && NODE_ENV=test npm run sync:es && NODE_ENV=test npm run sync:db && NODE_ENV=test ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- --timeout 10000 --compilers js:babel-core/register $(find src -path '*spec.js*')", "test:watch": "NODE_ENV=test ./node_modules/.bin/mocha -w --compilers js:babel-core/register $(find src -path '*spec.js*')", - "seed": "babel-node src/tests/seed.js --presets es2015" + "seed": "babel-node src/tests/seed.js --presets es2015", + "demo-data": "babel-node local/seed" }, "repository": { "type": "git", From ed3e6254eb0a8ab3530a3f68673e9ca6f9ad9a51 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Sat, 16 Mar 2019 17:37:20 +0800 Subject: [PATCH 2/2] skip linting for seed scripts - this should be fixed --- .eslintignore | 1 + local/seed/seedMetadata.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index 7674d60e..87d8ad2d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,7 @@ config/local.js config/mock.local.js config/m2m.local.js +local/seed/ node_modules dist .ebextensions diff --git a/local/seed/seedMetadata.js b/local/seed/seedMetadata.js index 5cfef2a3..eca3aa20 100644 --- a/local/seed/seedMetadata.js +++ b/local/seed/seedMetadata.js @@ -1,4 +1,3 @@ -/* eslint-disable */ const _ = require('lodash') const axios = require('axios'); const Promise = require('bluebird');