@miso.ai/doggoganger 0.9.0-beta.8 → 0.9.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/.github/workflows/npm-publish.yml +1 -0
- package/bin/data.js +15 -0
- package/cli/index.js +7 -6
- package/dist/umd/doggoganger-browser.min.js +1 -0
- package/package.json +14 -4
- package/rollup.config.dev.mjs +3 -0
- package/rollup.config.mjs +3 -0
- package/rollup.util.mjs +42 -0
- package/src/api/ask.js +47 -78
- package/src/api/index.js +14 -17
- package/src/api/interactions.js +10 -6
- package/src/api/products.js +28 -0
- package/src/api/recommendation.js +18 -6
- package/src/api/search.js +12 -5
- package/src/browser.js +5 -0
- package/src/data/answers.js +33 -0
- package/src/data/articles.js +1 -1
- package/src/data/fields.js +19 -3
- package/src/data/index.js +2 -0
- package/src/data/lorem.js +18 -20
- package/src/data/markdown/index.js +49 -21
- package/src/data/markdown/languages.js +101 -0
- package/src/data/questions.js +11 -0
- package/src/data/utils.js +21 -0
- package/src/data/words.js +182 -0
- package/src/index.js +29 -3
- package/src/middleware/error.js +29 -0
- package/src/middleware/index.js +2 -0
- package/src/middleware/latency.js +39 -0
- package/src/route/ask.js +37 -0
- package/src/route/index.js +26 -0
- package/src/route/interactions.js +17 -0
- package/src/route/products.js +33 -0
- package/src/route/recommendation.js +22 -0
- package/src/route/search.js +15 -0
- package/src/route/utils.js +6 -0
- package/src/utils.js +17 -0
- package/src/api/handlers.js +0 -10
- /package/{src/data → data}/words.yaml +0 -0
|
@@ -1,24 +1,55 @@
|
|
|
1
1
|
import { randomInt, imageUrl, shuffle } from '../utils.js';
|
|
2
2
|
import * as lorem from '../lorem.js';
|
|
3
|
+
import * as languages from './languages.js';
|
|
3
4
|
|
|
4
5
|
// TODO: wild mode that generates edge cases
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
function extractLangFeatures(features = []) {
|
|
8
|
+
const languages = new Set();
|
|
9
|
+
const rest = [];
|
|
10
|
+
for (const feature of features) {
|
|
11
|
+
if (feature.startsWith('lang-')) {
|
|
12
|
+
let lang = feature.slice(5);
|
|
13
|
+
if (lang === 'javascript') {
|
|
14
|
+
lang = 'js';
|
|
15
|
+
}
|
|
16
|
+
languages.add(lang);
|
|
17
|
+
} else {
|
|
18
|
+
rest.push(feature);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [[...languages], rest];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function markdown({ features, blocks = [8, 12], sampling = 1 } = {}) {
|
|
25
|
+
let languages = [];
|
|
26
|
+
[languages, features] = extractLangFeatures(features);
|
|
7
27
|
// TODO: block features
|
|
8
|
-
return [
|
|
9
|
-
atxHeading({ features }),
|
|
10
|
-
paragraph({ features }),
|
|
11
|
-
fencedCodeBlock({ features }),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
return sample([
|
|
29
|
+
() => atxHeading({ features }),
|
|
30
|
+
() => paragraph({ features }),
|
|
31
|
+
...languages.map(lang => () => fencedCodeBlock({ lang, features })),
|
|
32
|
+
...(languages.length ? [] : [() => fencedCodeBlock({ features })]),
|
|
33
|
+
() => paragraph({ features }),
|
|
34
|
+
() => table({ features }),
|
|
35
|
+
() => image(),
|
|
36
|
+
() => paragraph({ features }),
|
|
37
|
+
() => hr(),
|
|
38
|
+
() => atxHeading({ features }),
|
|
39
|
+
() => paragraph({ features }),
|
|
40
|
+
() => list({ features }),
|
|
41
|
+
() => paragraph({ features }),
|
|
42
|
+
], sampling).join('\n\n');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function sample(fns, sampling) {
|
|
46
|
+
const arr = [];
|
|
47
|
+
for (const fn of fns) {
|
|
48
|
+
if (sampling >= 1 || Math.random() < sampling) {
|
|
49
|
+
arr.push(fn());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return arr;
|
|
22
53
|
}
|
|
23
54
|
|
|
24
55
|
// leaf blocks //
|
|
@@ -57,7 +88,8 @@ export function fencedCodeBlock({ lang, content, size, fenceChar = '`' }) {
|
|
|
57
88
|
}
|
|
58
89
|
|
|
59
90
|
export function paragraph({ features, size = [20, 50] }) {
|
|
60
|
-
|
|
91
|
+
// force all inline features
|
|
92
|
+
return lorem.lorem({ size, decorates: ['description', decorate()] });
|
|
61
93
|
}
|
|
62
94
|
|
|
63
95
|
export function table({ features, columns = [2, 4], rows = [2, 8] }) {
|
|
@@ -214,11 +246,7 @@ function listItemPrefix(type, checked = 'random') {
|
|
|
214
246
|
}
|
|
215
247
|
|
|
216
248
|
function codeContent({ lang, size = [10, 30] }) {
|
|
217
|
-
|
|
218
|
-
switch (lang) {
|
|
219
|
-
default:
|
|
220
|
-
return lorem.lorem({ output: 'multiline', size });
|
|
221
|
-
}
|
|
249
|
+
return lang && languages[lang] ? languages[lang]() : lorem.lorem({ output: 'multiline', size });
|
|
222
250
|
}
|
|
223
251
|
|
|
224
252
|
function tableRow(cells) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export function javascript() {
|
|
2
|
+
return JAVASCRIPT;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function js() {
|
|
6
|
+
return JAVASCRIPT;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const JAVASCRIPT = `
|
|
10
|
+
// module
|
|
11
|
+
import { a, b } from './module';
|
|
12
|
+
import * as module from './module';
|
|
13
|
+
import module from './module';
|
|
14
|
+
export default module;
|
|
15
|
+
export const a = 0;
|
|
16
|
+
export * from './module';
|
|
17
|
+
export * as module from './module';
|
|
18
|
+
|
|
19
|
+
// variables
|
|
20
|
+
let a = 10;
|
|
21
|
+
const b = 20;
|
|
22
|
+
|
|
23
|
+
// function declaration
|
|
24
|
+
function sum(x, y) {
|
|
25
|
+
return x + y;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// generator function
|
|
29
|
+
function* iterator() {
|
|
30
|
+
yield 0;
|
|
31
|
+
yield 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// arrow function
|
|
35
|
+
const multiply = (x, y) => x * y;
|
|
36
|
+
|
|
37
|
+
// class
|
|
38
|
+
class Person {
|
|
39
|
+
constructor(name, age) {
|
|
40
|
+
this.name = name;
|
|
41
|
+
this.age = age;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
greet() {
|
|
45
|
+
console.log('Hello, my name is Miso.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// primitive
|
|
50
|
+
const str = 'Hello, world!';
|
|
51
|
+
const num = 10.99;
|
|
52
|
+
|
|
53
|
+
// object
|
|
54
|
+
const person = new Person('John', 30);
|
|
55
|
+
person.greet();
|
|
56
|
+
|
|
57
|
+
// object literal
|
|
58
|
+
const object = {
|
|
59
|
+
name: 'John',
|
|
60
|
+
[x]: 10,
|
|
61
|
+
...props,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// array literal
|
|
65
|
+
const arr = [1, 2, 3, 4, 5, ...props];
|
|
66
|
+
|
|
67
|
+
// regexp literal
|
|
68
|
+
const regexp = /\\w+/g;
|
|
69
|
+
|
|
70
|
+
// operators
|
|
71
|
+
const sum = a + b;
|
|
72
|
+
const product = a * b;
|
|
73
|
+
const negation = -a;
|
|
74
|
+
const max = a > b ? a : b;
|
|
75
|
+
|
|
76
|
+
// flow control
|
|
77
|
+
let i = 9;
|
|
78
|
+
for (const n of arr) {
|
|
79
|
+
if (n > i) {
|
|
80
|
+
console.log(n);
|
|
81
|
+
}
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// async/await
|
|
86
|
+
(async () => {
|
|
87
|
+
const result = await asyncFunction();
|
|
88
|
+
})();
|
|
89
|
+
|
|
90
|
+
// try/catch
|
|
91
|
+
try {
|
|
92
|
+
} catch (e) {
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// destructuring
|
|
96
|
+
const { name, age, ...rest } = person;
|
|
97
|
+
const [ x, y, ...rest ] = arr;
|
|
98
|
+
|
|
99
|
+
// template literals
|
|
100
|
+
${'console.log(`The sum of ${a} and ${b} is ${sum(a, b)}.`);'}
|
|
101
|
+
`.trim();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as fields from './fields.js';
|
|
2
|
+
|
|
3
|
+
export function *questions({ rows, ...options } = {}) {
|
|
4
|
+
for (let i = 0; i < rows; i++) {
|
|
5
|
+
yield question({ ...options, index: i });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function question({} = {}) {
|
|
10
|
+
return fields.description({ size: [4, 8], punctuation: '?' });
|
|
11
|
+
}
|
package/src/data/utils.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export function uuid() {
|
|
2
|
+
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, a => (a ^ Math.random() * 16 >> a / 4).toString(16));
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
export function randomInt(min, max) {
|
|
2
6
|
return max == null || (max <= min) ? min : (min + Math.floor(Math.random() * (max - min)));
|
|
3
7
|
}
|
|
@@ -29,3 +33,20 @@ export function imageUrl(size) {
|
|
|
29
33
|
const sizePath = Array.isArray(size) ? size.length > 1 ? `${size[0]}/${size[1]}` : `${size[0]}` : `${size}`;
|
|
30
34
|
return `https://picsum.photos/seed/${seed}/${sizePath}`;
|
|
31
35
|
}
|
|
36
|
+
|
|
37
|
+
export function formatDatetime(timestamp) {
|
|
38
|
+
const str = new Date(timestamp).toISOString();
|
|
39
|
+
return str.endsWith('Z') ? str.slice(0, -1) : str;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function sample(size, sampling) {
|
|
43
|
+
return sampling !== undefined ? Math.ceil(size * sampling) : size;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function gaussRandom() {
|
|
47
|
+
return uniformRandom() + uniformRandom() + uniformRandom();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function uniformRandom() {
|
|
51
|
+
return Math.random() * 2 - 1;
|
|
52
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
|
|
2
|
+
// genereated by bin/data.js
|
|
3
|
+
export default [
|
|
4
|
+
"lorem",
|
|
5
|
+
"ipsum",
|
|
6
|
+
"dolor",
|
|
7
|
+
"sit",
|
|
8
|
+
"amet",
|
|
9
|
+
"consectetur",
|
|
10
|
+
"adipiscing",
|
|
11
|
+
"elit",
|
|
12
|
+
"curabitur",
|
|
13
|
+
"vel",
|
|
14
|
+
"hendrerit",
|
|
15
|
+
"libero",
|
|
16
|
+
"eleifend",
|
|
17
|
+
"blandit",
|
|
18
|
+
"nunc",
|
|
19
|
+
"ornare",
|
|
20
|
+
"odio",
|
|
21
|
+
"ut",
|
|
22
|
+
"orci",
|
|
23
|
+
"gravida",
|
|
24
|
+
"imperdiet",
|
|
25
|
+
"nullam",
|
|
26
|
+
"purus",
|
|
27
|
+
"lacinia",
|
|
28
|
+
"a",
|
|
29
|
+
"pretium",
|
|
30
|
+
"quis",
|
|
31
|
+
"congue",
|
|
32
|
+
"praesent",
|
|
33
|
+
"sagittis",
|
|
34
|
+
"laoreet",
|
|
35
|
+
"auctor",
|
|
36
|
+
"mauris",
|
|
37
|
+
"non",
|
|
38
|
+
"velit",
|
|
39
|
+
"eros",
|
|
40
|
+
"dictum",
|
|
41
|
+
"proin",
|
|
42
|
+
"accumsan",
|
|
43
|
+
"sapien",
|
|
44
|
+
"nec",
|
|
45
|
+
"massa",
|
|
46
|
+
"volutpat",
|
|
47
|
+
"venenatis",
|
|
48
|
+
"sed",
|
|
49
|
+
"eu",
|
|
50
|
+
"molestie",
|
|
51
|
+
"lacus",
|
|
52
|
+
"quisque",
|
|
53
|
+
"porttitor",
|
|
54
|
+
"ligula",
|
|
55
|
+
"dui",
|
|
56
|
+
"mollis",
|
|
57
|
+
"tempus",
|
|
58
|
+
"at",
|
|
59
|
+
"magna",
|
|
60
|
+
"vestibulum",
|
|
61
|
+
"turpis",
|
|
62
|
+
"ac",
|
|
63
|
+
"diam",
|
|
64
|
+
"tincidunt",
|
|
65
|
+
"id",
|
|
66
|
+
"condimentum",
|
|
67
|
+
"enim",
|
|
68
|
+
"sodales",
|
|
69
|
+
"in",
|
|
70
|
+
"hac",
|
|
71
|
+
"habitasse",
|
|
72
|
+
"platea",
|
|
73
|
+
"dictumst",
|
|
74
|
+
"aenean",
|
|
75
|
+
"neque",
|
|
76
|
+
"fusce",
|
|
77
|
+
"augue",
|
|
78
|
+
"leo",
|
|
79
|
+
"eget",
|
|
80
|
+
"semper",
|
|
81
|
+
"mattis",
|
|
82
|
+
"tortor",
|
|
83
|
+
"scelerisque",
|
|
84
|
+
"nulla",
|
|
85
|
+
"interdum",
|
|
86
|
+
"tellus",
|
|
87
|
+
"malesuada",
|
|
88
|
+
"rhoncus",
|
|
89
|
+
"porta",
|
|
90
|
+
"sem",
|
|
91
|
+
"aliquet",
|
|
92
|
+
"et",
|
|
93
|
+
"nam",
|
|
94
|
+
"suspendisse",
|
|
95
|
+
"potenti",
|
|
96
|
+
"vivamus",
|
|
97
|
+
"luctus",
|
|
98
|
+
"fringilla",
|
|
99
|
+
"erat",
|
|
100
|
+
"donec",
|
|
101
|
+
"justo",
|
|
102
|
+
"vehicula",
|
|
103
|
+
"ultricies",
|
|
104
|
+
"varius",
|
|
105
|
+
"ante",
|
|
106
|
+
"primis",
|
|
107
|
+
"faucibus",
|
|
108
|
+
"ultrices",
|
|
109
|
+
"posuere",
|
|
110
|
+
"cubilia",
|
|
111
|
+
"curae",
|
|
112
|
+
"etiam",
|
|
113
|
+
"cursus",
|
|
114
|
+
"aliquam",
|
|
115
|
+
"quam",
|
|
116
|
+
"dapibus",
|
|
117
|
+
"nisl",
|
|
118
|
+
"feugiat",
|
|
119
|
+
"egestas",
|
|
120
|
+
"class",
|
|
121
|
+
"aptent",
|
|
122
|
+
"taciti",
|
|
123
|
+
"sociosqu",
|
|
124
|
+
"ad",
|
|
125
|
+
"litora",
|
|
126
|
+
"torquent",
|
|
127
|
+
"per",
|
|
128
|
+
"conubia",
|
|
129
|
+
"nostra",
|
|
130
|
+
"inceptos",
|
|
131
|
+
"himenaeos",
|
|
132
|
+
"phasellus",
|
|
133
|
+
"nibh",
|
|
134
|
+
"pulvinar",
|
|
135
|
+
"vitae",
|
|
136
|
+
"urna",
|
|
137
|
+
"iaculis",
|
|
138
|
+
"lobortis",
|
|
139
|
+
"nisi",
|
|
140
|
+
"viverra",
|
|
141
|
+
"arcu",
|
|
142
|
+
"morbi",
|
|
143
|
+
"pellentesque",
|
|
144
|
+
"metus",
|
|
145
|
+
"commodo",
|
|
146
|
+
"ut",
|
|
147
|
+
"facilisis",
|
|
148
|
+
"felis",
|
|
149
|
+
"tristique",
|
|
150
|
+
"ullamcorper",
|
|
151
|
+
"placerat",
|
|
152
|
+
"aenean",
|
|
153
|
+
"convallis",
|
|
154
|
+
"sollicitudin",
|
|
155
|
+
"integer",
|
|
156
|
+
"rutrum",
|
|
157
|
+
"duis",
|
|
158
|
+
"est",
|
|
159
|
+
"etiam",
|
|
160
|
+
"bibendum",
|
|
161
|
+
"donec",
|
|
162
|
+
"pharetra",
|
|
163
|
+
"vulputate",
|
|
164
|
+
"maecenas",
|
|
165
|
+
"mi",
|
|
166
|
+
"fermentum",
|
|
167
|
+
"consequat",
|
|
168
|
+
"suscipit",
|
|
169
|
+
"aliquam",
|
|
170
|
+
"habitant",
|
|
171
|
+
"senectus",
|
|
172
|
+
"netus",
|
|
173
|
+
"fames",
|
|
174
|
+
"quisque",
|
|
175
|
+
"euismod",
|
|
176
|
+
"curabitur",
|
|
177
|
+
"lectus",
|
|
178
|
+
"elementum",
|
|
179
|
+
"tempor",
|
|
180
|
+
"risus",
|
|
181
|
+
"cras"
|
|
182
|
+
];
|
package/src/index.js
CHANGED
|
@@ -3,15 +3,17 @@ import Router from '@koa/router';
|
|
|
3
3
|
import cors from '@koa/cors';
|
|
4
4
|
import serveStatic from 'koa-static';
|
|
5
5
|
import { koaBody } from 'koa-body';
|
|
6
|
-
import
|
|
6
|
+
import _route from './route/index.js';
|
|
7
|
+
import Api from './api/index.js';
|
|
7
8
|
import { exclusion } from './utils.js';
|
|
8
9
|
|
|
9
10
|
export default function doggoganger({ port = 9901, serve = false, ...options } = {}) {
|
|
10
11
|
const app = new Koa();
|
|
11
12
|
const router = new Router();
|
|
12
|
-
const api =
|
|
13
|
+
const api = _route(new Api(options), options);
|
|
13
14
|
|
|
14
15
|
router.use('/api', api.routes(), api.allowedMethods());
|
|
16
|
+
router.use('/v1', api.routes(), api.allowedMethods());
|
|
15
17
|
|
|
16
18
|
if (serve) {
|
|
17
19
|
app
|
|
@@ -21,8 +23,32 @@ export default function doggoganger({ port = 9901, serve = false, ...options } =
|
|
|
21
23
|
|
|
22
24
|
app
|
|
23
25
|
.use(cors())
|
|
24
|
-
.use(koaBody(
|
|
26
|
+
.use(koaBody({
|
|
27
|
+
formLimit: '100mb',
|
|
28
|
+
textLimit: '100mb',
|
|
29
|
+
jsonLimit: '100mb',
|
|
30
|
+
onerror: function (err, ctx) {
|
|
31
|
+
console.error(err);
|
|
32
|
+
ctx.throw('body parse error', 422);
|
|
33
|
+
},
|
|
34
|
+
}))
|
|
35
|
+
.use(handleAllPath(options))
|
|
25
36
|
.use(router.routes())
|
|
26
37
|
.use(router.allowedMethods())
|
|
38
|
+
.use(handleUnrecognizedPath(options))
|
|
27
39
|
.listen(port);
|
|
28
40
|
}
|
|
41
|
+
|
|
42
|
+
function handleAllPath({ verbose } = {}) {
|
|
43
|
+
return async (ctx, next) => {
|
|
44
|
+
verbose && console.log(`${ctx.method} ${ctx.url}`);
|
|
45
|
+
await next();
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function handleUnrecognizedPath({ verbose } = {}) {
|
|
50
|
+
return async (ctx, next) => {
|
|
51
|
+
verbose && console.log(`Unrecognized path: ${ctx.method} ${ctx.url}`);
|
|
52
|
+
await next();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { trimObj } from '../utils.js';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_OPTIONS = {
|
|
4
|
+
rate: 0,
|
|
5
|
+
verbose: false,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export default function error(options) {
|
|
9
|
+
const globalOptions = {
|
|
10
|
+
...DEFAULT_OPTIONS,
|
|
11
|
+
...trimObj(options),
|
|
12
|
+
};
|
|
13
|
+
return async (ctx, next) => {
|
|
14
|
+
const { rate, verbose } = {
|
|
15
|
+
...globalOptions,
|
|
16
|
+
...getOptionsFromCtx(ctx),
|
|
17
|
+
};
|
|
18
|
+
if (rate && Math.random() < rate) {
|
|
19
|
+
verbose && console.log(`Simulate error`);
|
|
20
|
+
throw new Error();
|
|
21
|
+
}
|
|
22
|
+
await next();
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getOptionsFromCtx(ctx) {
|
|
27
|
+
const rate = Number(ctx.get('x-error-rate')) || undefined;
|
|
28
|
+
return trimObj({ rate });
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { delay, trimObj } from '../utils.js';
|
|
2
|
+
import { gaussRandom } from '../data/utils.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_OPTIONS = {
|
|
5
|
+
enabled: true,
|
|
6
|
+
verbose: false,
|
|
7
|
+
min: 200,
|
|
8
|
+
max: 2000,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function latency(options) {
|
|
12
|
+
const globalOptions = {
|
|
13
|
+
...DEFAULT_OPTIONS,
|
|
14
|
+
...trimObj(options),
|
|
15
|
+
};
|
|
16
|
+
return async (ctx, next) => {
|
|
17
|
+
const { enabled, min, max, verbose } = {
|
|
18
|
+
...globalOptions,
|
|
19
|
+
...getOptionsFromCtx(ctx),
|
|
20
|
+
};
|
|
21
|
+
if (enabled) {
|
|
22
|
+
const time = (min + max) / 2 + gaussRandom() * (max - min) / 6;
|
|
23
|
+
verbose && console.log(`Add latency: ${time}ms`);
|
|
24
|
+
await delay(time);
|
|
25
|
+
}
|
|
26
|
+
await next();
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getOptionsFromCtx(ctx) {
|
|
31
|
+
const latencyStr = ctx.get('x-latency') || undefined;
|
|
32
|
+
if (latencyStr === '0' || latencyStr === 'false') {
|
|
33
|
+
return { enabled: false };
|
|
34
|
+
}
|
|
35
|
+
const value = Number(latencyStr) || undefined;
|
|
36
|
+
const min = value || Number(ctx.get('x-latency-min')) || undefined;
|
|
37
|
+
const max = value || Number(ctx.get('x-latency-max')) || undefined;
|
|
38
|
+
return trimObj({ min, max });
|
|
39
|
+
}
|
package/src/route/ask.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
function getOptionsFromCtx(ctx) {
|
|
5
|
+
const speedRate = Number(ctx.get('x-speed-rate')) || undefined;
|
|
6
|
+
const answerFormat = ctx.get('x-answer-format') || undefined;
|
|
7
|
+
const answerSampling = Number(ctx.get('x-answer-sampling')) || undefined;
|
|
8
|
+
const answerLanguagesStr = ctx.get('x-answer-languages') || undefined;
|
|
9
|
+
const answerLanguages = answerLanguagesStr ? answerLanguagesStr.split(',') : undefined;
|
|
10
|
+
return { answerFormat, answerSampling, answerLanguages, speedRate };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function(api) {
|
|
14
|
+
const answers = new Map();
|
|
15
|
+
const router = new Router();
|
|
16
|
+
|
|
17
|
+
router.post('/questions', (ctx) => {
|
|
18
|
+
const { question, parent_question_id } = parseBodyIfNecessary(ctx.request.body);
|
|
19
|
+
const answer = api.ask.questions({ question, parent_question_id }, getOptionsFromCtx(ctx));
|
|
20
|
+
const { question_id } = answer;
|
|
21
|
+
answers.set(question_id, answer);
|
|
22
|
+
ctx.body = JSON.stringify({ data: { question_id } });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
router.get('/questions/:id/answer', (ctx) => {
|
|
26
|
+
const { id } = ctx.params;
|
|
27
|
+
const answer = answers.get(id);
|
|
28
|
+
if (!answer) {
|
|
29
|
+
ctx.status = 404;
|
|
30
|
+
} else {
|
|
31
|
+
const data = answer.get();
|
|
32
|
+
ctx.body = JSON.stringify({ data });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return router;
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import ask from './ask.js';
|
|
3
|
+
import recommendation from './recommendation.js';
|
|
4
|
+
import search from './search.js';
|
|
5
|
+
import interactions from './interactions.js';
|
|
6
|
+
import products from './products.js';
|
|
7
|
+
import { latency, error } from '../middleware/index.js';
|
|
8
|
+
|
|
9
|
+
function use(router, path, middleware) {
|
|
10
|
+
router.use(path, middleware.routes(), middleware.allowedMethods());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function(api, options) {
|
|
14
|
+
const router = new Router();
|
|
15
|
+
|
|
16
|
+
router.use(latency(options.latency));
|
|
17
|
+
//router.use(error(options.error));
|
|
18
|
+
|
|
19
|
+
use(router, '/ask', ask(api));
|
|
20
|
+
use(router, '/recommendation', recommendation(api));
|
|
21
|
+
use(router, '/search', search(api));
|
|
22
|
+
use(router, '/interactions', interactions(api));
|
|
23
|
+
use(router, '/products', products(api));
|
|
24
|
+
|
|
25
|
+
return router;
|
|
26
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export default function(api) {
|
|
5
|
+
const router = new Router();
|
|
6
|
+
|
|
7
|
+
router.post('/', (ctx) => {
|
|
8
|
+
const { data } = parseBodyIfNecessary(ctx.request.body);
|
|
9
|
+
ctx.body = {
|
|
10
|
+
took: 5,
|
|
11
|
+
errors: false,
|
|
12
|
+
data: api.interactions.upload(data),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return router;
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export default function(api) {
|
|
5
|
+
const router = new Router();
|
|
6
|
+
|
|
7
|
+
router.post('/', (ctx) => {
|
|
8
|
+
const { data } = parseBodyIfNecessary(ctx.request.body);
|
|
9
|
+
ctx.body = {
|
|
10
|
+
took: 5,
|
|
11
|
+
errors: false,
|
|
12
|
+
data: api.products.upload(data),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
router.get('/_ids', (ctx) => {
|
|
17
|
+
ctx.body = {
|
|
18
|
+
message: 'success',
|
|
19
|
+
data: api.products.ids(),
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
router.get('/_status/:taskId', (ctx) => {
|
|
24
|
+
ctx.body = {
|
|
25
|
+
took: 100,
|
|
26
|
+
errors: false,
|
|
27
|
+
data: [],
|
|
28
|
+
code: 200,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return router;
|
|
33
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export default function(api) {
|
|
5
|
+
const router = new Router();
|
|
6
|
+
|
|
7
|
+
router.post('/user_to_products', (ctx) => {
|
|
8
|
+
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
9
|
+
ctx.body = {
|
|
10
|
+
data: api.recommendation.user_to_products({ rows }),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
router.post('/product_to_products', (ctx) => {
|
|
15
|
+
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
16
|
+
ctx.body = {
|
|
17
|
+
data: api.recommendation.product_to_products({ rows }),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return router;
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Router from '@koa/router';
|
|
2
|
+
import { parseBodyIfNecessary } from './utils.js';
|
|
3
|
+
|
|
4
|
+
export default function(api) {
|
|
5
|
+
const router = new Router();
|
|
6
|
+
|
|
7
|
+
router.post('/search', (ctx) => {
|
|
8
|
+
const { rows = 5 } = parseBodyIfNecessary(ctx.request.body);
|
|
9
|
+
ctx.body = {
|
|
10
|
+
data: api.search.search({ rows }),
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return router;
|
|
15
|
+
}
|