@vida-global/core 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/index.js +17 -0
- package/lib/active_record/README.md +205 -0
- package/lib/active_record/baseRecord.js +112 -0
- package/lib/active_record/db/connection.js +128 -0
- package/lib/active_record/db/connectionConfiguration.js +114 -0
- package/lib/active_record/db/importSchema.js +4 -0
- package/lib/active_record/db/migration.js +132 -0
- package/lib/active_record/db/migrationTemplate.js +8 -0
- package/lib/active_record/db/migrationVersion.js +68 -0
- package/lib/active_record/db/migrator.js +169 -0
- package/lib/active_record/db/queryInterface.js +47 -0
- package/lib/active_record/db/schema.js +113 -0
- package/lib/active_record/index.js +6 -0
- package/lib/active_record/utils.js +43 -0
- package/lib/http/README.md +32 -0
- package/lib/http/client.js +129 -0
- package/lib/http/error.js +34 -0
- package/lib/logger/README.md +2 -0
- package/lib/logger/index.js +16 -0
- package/lib/release/develop.js +27 -0
- package/lib/release/git.js +86 -0
- package/lib/release/increment.js +56 -0
- package/lib/release/index.js +10 -0
- package/lib/release/release.js +30 -0
- package/lib/release/utils.js +44 -0
- package/lib/server/README.md +37 -0
- package/lib/server/index.js +9 -0
- package/lib/server/server.js +359 -0
- package/lib/server/serverController.js +344 -0
- package/lib/server/systemController.js +23 -0
- package/package.json +37 -0
- package/scripts/active_record/migrate.js +30 -0
- package/scripts/release.js +62 -0
- package/test/active_record/baseRecord.test.js +179 -0
- package/test/active_record/db/connection.test.js +221 -0
- package/test/active_record/db/connectionConfiguration.test.js +184 -0
- package/test/active_record/db/migrator.test.js +266 -0
- package/test/active_record/db/queryInterface.test.js +66 -0
- package/test/http/client.test.js +271 -0
- package/test/http/error.test.js +71 -0
- package/test/release/develop.test.js +57 -0
- package/test/release/git.test.js +189 -0
- package/test/release/increment.test.js +145 -0
- package/test/release/release.test.js +72 -0
- package/test/release/utils.test.js +148 -0
- package/test/server/helpers/controllers/barController.js +9 -0
- package/test/server/helpers/controllers/fooController.js +48 -0
- package/test/server/helpers/controllers/sub/bazController.js +10 -0
- package/test/server/helpers/server.js +14 -0
- package/test/server/server.test.js +188 -0
- package/test/server/serverController.test.js +251 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const { logger } = require('../logger');
|
|
2
|
+
const { HttpError } = require('./error');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HttpClient {
|
|
6
|
+
async get(endpoint, { requestParams, headers }={}) {
|
|
7
|
+
return await this.#makeRequest(endpoint, "GET", arguments[1]);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async post(endpoint, { body, headers }={}) {
|
|
12
|
+
return await this.#makeRequest(endpoint, "POST", arguments[1]);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async put(endpoint, { body, headers }={}) {
|
|
17
|
+
return await this.#makeRequest(endpoint, "PUT", arguments[1]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async delete(endpoint, { requestParams, headers }={}) {
|
|
22
|
+
return await this.#makeRequest(endpoint, "DELETE", arguments[1]);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async #makeRequest(endpoint, method, { requestParams, body, headers }={}) {
|
|
27
|
+
this.#logRequest(method, endpoint);
|
|
28
|
+
|
|
29
|
+
endpoint = `${this.urlRoot}${endpoint}`;
|
|
30
|
+
const url = new URL(endpoint);
|
|
31
|
+
const requestData = structuredClone(requestParams || body || {});
|
|
32
|
+
const payload = this.#prepareRequestPayload(url, requestData, headers, method);
|
|
33
|
+
|
|
34
|
+
const res = await fetch(url.toString(), payload);
|
|
35
|
+
|
|
36
|
+
if (res.status && res.status < 300) {
|
|
37
|
+
const json = await res.json();
|
|
38
|
+
return {data: json, status: res.status};
|
|
39
|
+
} else {
|
|
40
|
+
throw new HttpError(res, payload);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
#prepareRequestPayload(url, requestData, headers, method) {
|
|
46
|
+
const payload = {};
|
|
47
|
+
|
|
48
|
+
headers ||= {};
|
|
49
|
+
payload.headers = {...this.defaultHeaders, ...headers};
|
|
50
|
+
payload.method = method;
|
|
51
|
+
this.#addRequestData(payload, requestData, method, url, payload.headers);
|
|
52
|
+
|
|
53
|
+
return payload;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
#addRequestData(payload, requestData, method, url, headers) {
|
|
58
|
+
if (method == "POST" || method == "PUT") {
|
|
59
|
+
payload.body = JSON.stringify(requestData || {});
|
|
60
|
+
} else {
|
|
61
|
+
this.#addUrlRequestData(url, requestData, headers);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#addUrlRequestData(url, requestData, headers) {
|
|
66
|
+
const contentType = headers['Content-Type'];
|
|
67
|
+
if (contentType == 'application/json') {
|
|
68
|
+
this.#addJsonUrlRequestData(requestData, url);
|
|
69
|
+
} else if (contentType == 'application/x-www-form-urlencoded') {
|
|
70
|
+
this.#addFormUrlRequestData(requestData, url);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
#addJsonUrlRequestData(requestData, url) {
|
|
76
|
+
Object.entries(requestData).forEach(([paramName, val]) => {
|
|
77
|
+
if (typeof val == 'object') {
|
|
78
|
+
val = JSON.stringify(val);
|
|
79
|
+
}
|
|
80
|
+
url.searchParams.set(paramName, val);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
#addFormUrlRequestData(requestData, url) {
|
|
86
|
+
Object.entries(requestData).forEach(([paramName, val]) => {
|
|
87
|
+
this.#appendUrlRequestValue(paramName, val, url)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
#appendUrlRequestValue(paramName, val, url) {
|
|
93
|
+
if (Array.isArray(val)) {
|
|
94
|
+
val.forEach(v => url.searchParams.append(paramName, v));
|
|
95
|
+
|
|
96
|
+
} else if (typeof val == 'object') {
|
|
97
|
+
for (const [subParamName, subValue] of Object.entries(val)) {
|
|
98
|
+
this.#appendUrlRequestValue(`${paramName}[${subParamName}]`, val, formData);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
} else {
|
|
102
|
+
url.searchParams.append(paramName, val);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
#logRequest(method, endpoint) {
|
|
108
|
+
logger.info(`API CALL: ${method} ${endpoint}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
get defaultHeaders() {
|
|
113
|
+
return {
|
|
114
|
+
Accept: "application/json",
|
|
115
|
+
"Content-Type": "application/json",
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
get urlRoot() {
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
module.exports = {
|
|
127
|
+
HttpClient,
|
|
128
|
+
HttpError
|
|
129
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class HttpError extends Error {
|
|
2
|
+
#requestPayload;
|
|
3
|
+
#response;
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
constructor(response, requestPayload) {
|
|
7
|
+
super();
|
|
8
|
+
this.#response = response;
|
|
9
|
+
this.#requestPayload = requestPayload;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
get message() { return `HTTP Error: ${this.status}`};
|
|
14
|
+
get status() { return this.#response.status; }
|
|
15
|
+
get statusText() { return this.#response.statusText; }
|
|
16
|
+
get responseHeaders() { return structuredClone(this.#response.headers); }
|
|
17
|
+
get requestPayload() { return structuredClone(this.#requestPayload); }
|
|
18
|
+
get url() { return this.#response.url; }
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async data() {
|
|
22
|
+
const body = await this.#response.text();
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(body);
|
|
25
|
+
} catch(err) {
|
|
26
|
+
return body;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
HttpError
|
|
34
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const pino = require('pino');
|
|
2
|
+
const logger = pino({
|
|
3
|
+
transport: {
|
|
4
|
+
level: process.env.LOG_LEVEL ? 'debug' : 'info',
|
|
5
|
+
target: 'pino-pretty',
|
|
6
|
+
options: {
|
|
7
|
+
colorize: true,
|
|
8
|
+
ignore: 'pid,hostname',
|
|
9
|
+
translateTime: 'SYS:standard',
|
|
10
|
+
messageFormat: '{msg}',
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module.exports = { logger };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const Git = require('./git');
|
|
2
|
+
const utils = require('./utils');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
async function develop(branchType, issueNumber, description, sourceBranch) {
|
|
6
|
+
const initials = devInitials();
|
|
7
|
+
const parsedDescription = description.replace(/\s+/g, '-').toLowerCase().replace(/[^a-z0-9-]/g, '');;
|
|
8
|
+
|
|
9
|
+
const branchName = `${branchType}/${issueNumber}/${initials}/${parsedDescription}`;
|
|
10
|
+
|
|
11
|
+
const question = `Do you want to create branch ${branchName}?`;
|
|
12
|
+
const _confirm = await utils.confirmContinue(question);
|
|
13
|
+
if (!_confirm) return;
|
|
14
|
+
|
|
15
|
+
Git.createBranch(branchName, sourceBranch);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function devInitials() {
|
|
20
|
+
const name = Git.getCurrentUser()
|
|
21
|
+
return name.split(' ').map(piece => piece[0]).join('').toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
develop
|
|
27
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function createBranch(branchName, branchToCopy=null) {
|
|
5
|
+
if (!branchToCopy) branchToCopy = getPrimaryBranch();
|
|
6
|
+
|
|
7
|
+
executeShellCommand(`git branch ${branchName} ${branchToCopy}`);
|
|
8
|
+
checkout(branchName)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
function getPrimaryBranch() {
|
|
13
|
+
const allBranches = branches();
|
|
14
|
+
if (allBranches.includes('origin/master')) return 'origin/master';
|
|
15
|
+
if (allBranches.includes('origin/main')) return 'origin/main';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
function checkout(branchName) {
|
|
20
|
+
executeShellCommand(`git checkout ${branchName}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function pull() {
|
|
25
|
+
executeShellCommand('git pull --prune');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
function push() {
|
|
30
|
+
const currBranch = getCurrentBranch();
|
|
31
|
+
executeShellCommand(`git push -u origin ${currBranch}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
function getCurrentBranch() {
|
|
36
|
+
const allBranches = branches(false);
|
|
37
|
+
return allBranches.find(b => /^\s*\*\s+/.test(b)).replace('*', '').trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
function forceRemotePush(sourceBranch, destBranch) {
|
|
42
|
+
executeShellCommand(`git push origin origin/${sourceBranch}:${destBranch} --force`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function add(file) {
|
|
47
|
+
executeShellCommand(`git add ${file}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
function commit(msg) {
|
|
52
|
+
executeShellCommand(`git commit -m '${msg}'`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function branches(remote=true) {
|
|
57
|
+
let cmd = 'git branch';
|
|
58
|
+
if (remote) cmd = `${cmd} -r`;
|
|
59
|
+
const branches = executeShellCommand(cmd);
|
|
60
|
+
return branches.toString().split("\n").map(b => b.trim());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
function getCurrentUser() {
|
|
65
|
+
return executeShellCommand(`git config user.name`).toString();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
function executeShellCommand(command) {
|
|
70
|
+
return execSync(command);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
add,
|
|
76
|
+
branches,
|
|
77
|
+
checkout,
|
|
78
|
+
commit,
|
|
79
|
+
createBranch,
|
|
80
|
+
forceRemotePush,
|
|
81
|
+
getCurrentBranch,
|
|
82
|
+
getCurrentUser,
|
|
83
|
+
getPrimaryBranch,
|
|
84
|
+
pull,
|
|
85
|
+
push
|
|
86
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const Git = require('./git');
|
|
3
|
+
const utils = require('./utils');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const packageJsonPath = `${process.cwd()}/package.json`;
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async function increment(type) {
|
|
10
|
+
Git.pull();
|
|
11
|
+
const currVersion = utils.getCurrentVersion();
|
|
12
|
+
const newVersion = getNewVersion(currVersion, type);
|
|
13
|
+
|
|
14
|
+
const _confirm = await utils.confirmContinue(`Do you want to create version ${newVersion}?`);
|
|
15
|
+
if (!_confirm) return;
|
|
16
|
+
|
|
17
|
+
Git.createBranch(`release/${newVersion}`);
|
|
18
|
+
updatePackageVersion(newVersion)
|
|
19
|
+
Git.push();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
function getNewVersion(currVersion, type) {
|
|
24
|
+
const newVersion = currVersion.map(v => parseInt(v));
|
|
25
|
+
switch(type) {
|
|
26
|
+
case 'major':
|
|
27
|
+
newVersion[0] += 1;
|
|
28
|
+
newVersion[1] = 0;
|
|
29
|
+
newVersion[2] = 0;
|
|
30
|
+
break;
|
|
31
|
+
case 'minor':
|
|
32
|
+
newVersion[1] += 1;
|
|
33
|
+
newVersion[2] = 0;
|
|
34
|
+
break;
|
|
35
|
+
case 'point':
|
|
36
|
+
newVersion[2] += 1;
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
throw new Error('Invalid increment type');
|
|
40
|
+
}
|
|
41
|
+
return newVersion.join('.');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
function updatePackageVersion(newVersion) {
|
|
46
|
+
let packageJson = fs.readFileSync(packageJsonPath, {encoding: 'utf8'});
|
|
47
|
+
packageJson = packageJson.replace(/"version":\s*"\d+\.\d+\.\d+"/, `"version": "${newVersion}"`);
|
|
48
|
+
fs.writeFileSync(packageJsonPath, packageJson);
|
|
49
|
+
Git.add(packageJsonPath);
|
|
50
|
+
Git.commit(`build: incrementing build to ${newVersion}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
increment
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const Git = require('./git');
|
|
2
|
+
const utils = require('./utils');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
async function release(env, versionToRelease=null) {
|
|
6
|
+
Git.pull();
|
|
7
|
+
|
|
8
|
+
if (versionToRelease) {
|
|
9
|
+
const branchName = `origin/release/${versionToRelease}`;
|
|
10
|
+
const allBranches = Git.branches();
|
|
11
|
+
if (!allBranches.includes(branchName)) {
|
|
12
|
+
throw new Error(`release/${versionToRelease} does not exist`);
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
versionToRelease = utils.getCurrentVersion().join('.');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const question = `Do you want to release version ${versionToRelease} to ${env}?`;
|
|
19
|
+
const _confirm = await utils.confirmContinue(question);
|
|
20
|
+
if (!_confirm) return;
|
|
21
|
+
|
|
22
|
+
const sourceBranch = `release/${versionToRelease}`;
|
|
23
|
+
const releaseBranch = `release/${env}`;
|
|
24
|
+
Git.forceRemotePush(sourceBranch, releaseBranch);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
release
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const Git = require('./git');
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function getCurrentVersion() {
|
|
5
|
+
const branches = Git.branches();
|
|
6
|
+
const releases = branches.filter(branchName => /release\/\d+\.\d+\.\d+/.test(branchName));
|
|
7
|
+
const versions = releases.map(branchName => {
|
|
8
|
+
return branchName.replace('origin/release/', '').split('.').map(n => parseInt(n));
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
versions.sort((v1,v2) => {
|
|
12
|
+
if (v1[0] != v2[0]) return v2[0] - v1[0];
|
|
13
|
+
if (v1[1] != v2[1]) return v2[1] - v1[1];
|
|
14
|
+
return v2[2] - v1[2];
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return versions[0] || [1,0,0];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async function confirmContinue(question) {
|
|
22
|
+
return await getInput(question, ['y','n']) == 'y';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async function getInput(question, options) {
|
|
27
|
+
const readline = require('node:readline/promises').createInterface({
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const response = await readline.question(`${question} [${options.join(',')}] `);
|
|
33
|
+
readline.close();
|
|
34
|
+
|
|
35
|
+
if (options.includes(response)) return response;
|
|
36
|
+
return getInput(question, options);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
confirmContinue,
|
|
42
|
+
getCurrentVersion,
|
|
43
|
+
getInput,
|
|
44
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# VidaServer
|
|
2
|
+
The `VidaServer` is a general purpose wrapper around an `express` server. It initializes standard middleware that we want running on all Vida application servers and leaves hooks for adding additional middleware on subclasses for specific use cases.
|
|
3
|
+
The `VidaServer` works in conjunction with the `VidaServerController`. When a `VidaServer` begins listening, it searches its directory structure (as defined in the `controllerDirectories` getter) for subclasses of `VidaServerController`. It then creates routes based on all methods of the controller that are prefixed with `get`, `post`, `put`, `patch`, or `delete`.
|
|
4
|
+
The paths for these routes are defined by the directory structure of the controller, the name of the controller, and the name of the method. For example, a method `getFoo` defined on a `BarController` in the directory `/controllers/baz`, will create the route `GET /baz/bar/foo`. “Index” will yield an empty path (e.g. `getIndex` in the previously described controller generates `GET /baz/bar`). However, the default paths can be overridden by defining the `routes` getter.
|
|
5
|
+
Each controller method will have direct access to request and response variables and a logger. Calling `render` will set the response body. It accepts either a string or a JSON object.
|
|
6
|
+
```
|
|
7
|
+
class UsersController extends ServerController {
|
|
8
|
+
// GET /users
|
|
9
|
+
getIndex() {
|
|
10
|
+
const users = getUsers(this.query.pageNumber, this.query.pageSize);
|
|
11
|
+
this.render(users.toJson());
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// PUT /users/foo
|
|
15
|
+
putFoo() {
|
|
16
|
+
this.logger.info("Putting Foo");
|
|
17
|
+
this.render("hello world");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// POST /user/:id
|
|
21
|
+
postUpdateUser() {
|
|
22
|
+
const user = getUser(this.params.id);
|
|
23
|
+
if (!user) {
|
|
24
|
+
this.status = 404;
|
|
25
|
+
this.render({error: "No user found"});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
user.update(this.body.user);
|
|
29
|
+
this.render({success: true});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static get routes() {
|
|
33
|
+
return {postUpdateUser: '/user/:id'};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|