Skip to content

Commit 393ecf1

Browse files
committed
feat(jsbattle): store challenge progress on server side
1 parent 3051661 commit 393ecf1

26 files changed

+776
-123
lines changed

packages/jsbattle-docs/docs/configuration.md

-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,6 @@ List of all settings of JsBattle:
128128
}
129129
```
130130

131-
132-
133131
## Environment variables
134132

135133
Part of the configuration could be provided as env vars. To disable this feature set `config.skipEnv` to `true`;
-1 Bytes
Loading
Loading
Loading
-1 Bytes
Loading
Loading
-2 Bytes
Loading
-2 Bytes
Loading
-2 Bytes
Loading

packages/jsbattle-mockserver/app/MockServer.js

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ class MockServer {
123123
"/api/profile": "/profile",
124124
"/api/:a/:b": "/:a,:b",
125125
"/api/:a/:b/:c": "/:a,:b/:c",
126+
"/api/:a/:b/:c/:d": "/:a,:b/:c/:d",
127+
"/api/:a/:b/:c/:d/:e": "/:a,:b/:c/:d/:e",
126128
"/api/*": "/$1"
127129
}))
128130
server.use(router)

packages/jsbattle-mockserver/app/init_db.json

+20
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,26 @@
239239
"modifiedAt": "2020-02-23T08:28:04.922Z"
240240
}
241241
],
242+
"user,challenges": [
243+
{
244+
"id": "challenge-8UCUaNvC",
245+
"challengeId": "challenge-8UCUaNvC",
246+
"code": "importScripts('lib/tank.js');\n\n// Don't know where to start?\n// Read Getting Started in \"Docs\" section \n\ntank.init(function(settings, info) {\n\t// initialize tank here\n \n});\n\ntank.loop(function(state, control) {\n\tcontrol.DEBUG = {msg: 'hello from 1st challenge'}\n \n});\n\n\n",
247+
"completed": true
248+
},
249+
{
250+
"id": "challenge-Du7tyrCB",
251+
"challengeId": "challenge-Du7tyrCB",
252+
"code": "importScripts('lib/tank.js');\n\n// Don't know where to start?\n// Read Getting Started in \"Docs\" section \n\ntank.init(function(settings, info) {\n\t// initialize tank here\n \n});\n\ntank.loop(function(state, control) {\n\tcontrol.DEBUG = {msg: 'how to solve that?'}\n \n});\n\n\n",
253+
"completed": false
254+
},
255+
{
256+
"id": "challenge-4syTf6ph",
257+
"challengeId": "challenge-4syTf6ph",
258+
"code": "importScripts('lib/tank.js');\n\n// Don't know where to start?\n// Read Getting Started in \"Docs\" section \n\ntank.init(function(settings, info) {\n\t// initialize tank here\n \n});\n\ntank.loop(function(state, control) {\n\tcontrol.DEBUG = {msg: 'locked'}\n \n});\n\n\n",
259+
"completed": false
260+
}
261+
],
242262
"authMethods": {
243263
"mock": {
244264
"name": "Mock",

packages/jsbattle-server/.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ module.exports = {
191191
"no-throw-literal": "error",
192192
"no-trailing-spaces": "error",
193193
"no-undef-init": "error",
194-
"no-undefined": "error",
194+
"no-undefined": "off",
195195
"no-unmodified-loop-condition": "error",
196196
"no-unneeded-ternary": "error",
197197
"no-unused-expressions": "error",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env node
2+
3+
// use that runner to work on jsbattle-webpage in dev mode.
4+
// Simply run
5+
// ```
6+
// $ packages/jsbattle-server/app/runner-weppage-dev-proxy.js
7+
// $ cd packages/jsbattle-webpage
8+
// $ npm run start:dev
9+
// ```
10+
11+
const Gateway = require('./Gateway.js');
12+
const path = require('path');
13+
14+
let gateway = new Gateway();
15+
let config = {
16+
"loglevel": "debug",
17+
"web": {
18+
"webroot": path.join(__dirname, 'public'),
19+
"host": "127.0.0.1",
20+
"baseUrl": "http://localhost:8080",
21+
"port": "9000",
22+
"gaCode": "AB-123456789-Z"
23+
},
24+
"auth": {
25+
"enabled": true,
26+
"admins": [
27+
{
28+
provider: 'github',
29+
username: 'jamro'
30+
},
31+
{
32+
provider: 'mock',
33+
username: 'mock'
34+
}
35+
],
36+
"providers": [{ "name": "mock" }]
37+
}
38+
};
39+
gateway.init(config).then(() => gateway.start())
40+
.catch(console.error);

packages/jsbattle-server/app/services/ApiGateway.service.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ class ApiGatewayService extends Service {
6464
"POST scripts": "scriptStore.createUserScript",
6565
"PATCH scripts/:id": "scriptStore.updateUserScript",
6666
"GET scripts/:id": "scriptStore.getUserScript",
67-
"DELETE scripts/:id": "scriptStore.deleteUserScript"
67+
"DELETE scripts/:id": "scriptStore.deleteUserScript",
68+
"GET challenges": "challenges.listUserChallanges",
69+
"GET challenges/:id": "challenges.getUserChallanges",
70+
"PATCH challenges/:id": "challenges.updateUserChallanges",
6871
},
6972
bodyParsers: {
7073
json: true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
const Service = require("moleculer").Service;
2+
const DbService = require("moleculer-db");
3+
const { ValidationError } = require("moleculer").Errors;
4+
const _ = require('lodash');
5+
const getDbAdapterConfig = require("../lib/getDbAdapterConfig.js");
6+
7+
class ChallengeService extends Service {
8+
9+
constructor(broker) {
10+
super(broker);
11+
let adapterConfig = getDbAdapterConfig(broker.serviceConfig.data, 'scriptStore')
12+
13+
this.parseServiceSchema({
14+
...adapterConfig,
15+
name: "challenges",
16+
mixins: [DbService],
17+
settings: {
18+
idField: 'id',
19+
fields: [
20+
"id",
21+
"challengeId",
22+
"userId",
23+
"code",
24+
"completed",
25+
"modifiedAt"
26+
],
27+
entityValidator: {
28+
userId: { type: "string", min: 1, max: 32 },
29+
challengeId: { type: "string", min: 1, max: 32 },
30+
completed: { type: "boolean", min: 1, max: 255 },
31+
code: { type: "string", min: 0, max: 65536 },
32+
modifiedAt: "date"
33+
}
34+
},
35+
dependencies: ['userStore'],
36+
actions: {
37+
listUserChallanges: this.listUserChallanges,
38+
getUserChallanges: this.getUserChallanges,
39+
updateUserChallanges: this.updateUserChallanges,
40+
},
41+
hooks: {
42+
before: {
43+
create: [
44+
function addDefaults(ctx) {
45+
const userId = ctx.meta.user ? ctx.meta.user.id : '';
46+
ctx.params.userId = ctx.params.userId || userId;
47+
ctx.params.completed = false;
48+
ctx.params.code = "importScripts('lib/tank.js');\n\n// Don't know where to start?\n// Read Getting Started in \"Docs\" section \n\ntank.init(function(settings, info) {\n\t// initialize tank here\n \n});\n\ntank.loop(function(state, control) {\n\t// write your tank logic here\n \n});\n\n\n";
49+
ctx.params.modifiedAt = new Date();
50+
ctx.params = _.omit(ctx.params, ['id']);
51+
return ctx;
52+
}
53+
],
54+
update: [
55+
function omitReadOnly(ctx) {
56+
ctx.params = _.omit(ctx.params, ['userId']);
57+
return ctx;
58+
}
59+
]
60+
}
61+
}
62+
});
63+
}
64+
65+
listUserChallanges(ctx) {
66+
const userId = ctx.meta.user ? ctx.meta.user.id : null;
67+
if(!userId) {
68+
throw new ValidationError('Not Authorized!', 401);
69+
}
70+
return ctx.call('challenges.list', {
71+
query: {
72+
userId: userId
73+
},
74+
fields: [
75+
"challengeId",
76+
"completed"
77+
]
78+
})
79+
}
80+
81+
async getUserChallanges(ctx) {
82+
const userId = ctx.meta.user ? ctx.meta.user.id : null;
83+
if(!userId) {
84+
throw new ValidationError('Not Authorized!', 401);
85+
}
86+
const challengeId = ctx.params.id
87+
let response = await ctx.call('challenges.find', {query: {
88+
userId: userId,
89+
challengeId: challengeId
90+
}});
91+
if(response.length > 0) {
92+
return response[0];
93+
}
94+
return ctx.call('challenges.create', {
95+
userId: userId,
96+
challengeId: challengeId
97+
});
98+
}
99+
100+
async updateUserChallanges(ctx) {
101+
const userId = ctx.meta.user ? ctx.meta.user.id : null;
102+
if(!userId) {
103+
throw new ValidationError('Not Authorized!', 401);
104+
}
105+
const challengeId = ctx.params.id
106+
let response = await ctx.call('challenges.find', {query: {
107+
userId: userId,
108+
challengeId: challengeId
109+
}});
110+
let challenge;
111+
if(response.length > 0) {
112+
challenge = response[0]
113+
} else {
114+
challenge = await ctx.call('challenges.create', {
115+
userId: userId,
116+
challengeId: challengeId
117+
});
118+
}
119+
120+
let updateData = {
121+
id: challenge.id,
122+
modifiedAt: new Date()
123+
}
124+
if(ctx.params.code !== undefined) {
125+
updateData.code = ctx.params.code;
126+
}
127+
if(ctx.params.completed !== undefined) {
128+
updateData.completed = ctx.params.completed;
129+
}
130+
131+
return ctx.call('challenges.update', updateData);
132+
133+
}
134+
135+
}
136+
137+
module.exports = ChallengeService;

0 commit comments

Comments
 (0)