nestia 3.1.1 → 3.1.2-dev.20221016
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 +133 -17
- package/lib/executable/internal/NestiaStarter.js +10 -26
- package/lib/executable/internal/NestiaStarter.js.map +1 -1
- package/package.json +2 -2
- package/template/.eslintrc.cjs +0 -22
- package/template/.github/workflows/build.yml +0 -18
- package/template/.prettierignore +0 -6
- package/template/.vscode/launch.json +0 -19
- package/template/.vscode/settings.json +0 -10
- package/template/LICENSE +0 -21
- package/template/README.md +0 -131
- package/template/bundle/gitignore +0 -6
- package/template/bundle/npmignore +0 -6
- package/template/nestia.config.ts +0 -12
- package/template/package.json +0 -62
- package/template/packages/api/LICENSE +0 -21
- package/template/packages/api/README.md +0 -66
- package/template/packages/api/package.json +0 -21
- package/template/prettier.config.js +0 -18
- package/template/src/Backend.ts +0 -65
- package/template/src/Configuration.ts +0 -11
- package/template/src/api/HttpError.ts +0 -1
- package/template/src/api/IConnection.ts +0 -1
- package/template/src/api/Primitive.ts +0 -1
- package/template/src/api/__internal/AesPkcs5.ts +0 -1
- package/template/src/api/__internal/Fetcher.ts +0 -1
- package/template/src/api/functional/bbs/articles/index.ts +0 -166
- package/template/src/api/functional/bbs/index.ts +0 -7
- package/template/src/api/functional/index.ts +0 -7
- package/template/src/api/index.ts +0 -4
- package/template/src/api/module.ts +0 -5
- package/template/src/api/structures/bbs/IBbsArticle.ts +0 -142
- package/template/src/api/structures/common/IAttachmentFile.ts +0 -5
- package/template/src/api/structures/common/IPage.ts +0 -75
- package/template/src/controllers/BbsArticlesController.ts +0 -46
- package/template/src/providers/bbs/BbsArticleProvider.ts +0 -217
- package/template/src/test/features/api/bbs/test_api_bbs_article_at.ts +0 -52
- package/template/src/test/features/api/bbs/test_api_bbs_article_index_search.ts +0 -82
- package/template/src/test/features/api/bbs/test_api_bbs_article_index_sort.ts +0 -49
- package/template/src/test/features/api/bbs/test_api_bbs_article_store.ts +0 -44
- package/template/src/test/features/api/bbs/test_api_bbs_article_update.ts +0 -66
- package/template/src/test/index.ts +0 -45
- package/template/src/test/internal/DynamicImportIterator.ts +0 -119
- package/template/src/test/internal/GaffComparator.ts +0 -48
- package/template/src/test/internal/IPointer.ts +0 -3
- package/template/src/test/internal/RandomGenerator.ts +0 -72
- package/template/src/test/internal/StopWatch.ts +0 -16
- package/template/src/test/internal/exception_must_be_thrown.ts +0 -11
- package/template/src/test/internal/validate_index.ts +0 -29
- package/template/src/test/internal/validate_index_sort.ts +0 -39
- package/template/src/utils/ArrayUtil.ts +0 -86
- package/template/src/utils/MapUtil.ts +0 -14
- package/template/tsconfig.api.json +0 -15
- package/template/tsconfig.json +0 -118
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { ForbiddenException, NotFoundException } from "@nestjs/common";
|
|
2
|
-
import { v4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
import { IBbsArticle } from "@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle";
|
|
5
|
-
import { IPage } from "@ORGANIZATION/PROJECT-api/lib/structures/common/IPage";
|
|
6
|
-
|
|
7
|
-
import { MapUtil } from "../../utils/MapUtil";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* This is a fake provider.
|
|
11
|
-
*
|
|
12
|
-
* Remove it or change it to be stored on the real DB.
|
|
13
|
-
*/
|
|
14
|
-
export namespace BbsArticleProvider {
|
|
15
|
-
export async function index(
|
|
16
|
-
section: string,
|
|
17
|
-
input: IBbsArticle.IRequest,
|
|
18
|
-
): Promise<IPage<IBbsArticle.ISummary>> {
|
|
19
|
-
// GET ENTIRE ARTICLES
|
|
20
|
-
const dict = storage.get(section);
|
|
21
|
-
if (dict === undefined)
|
|
22
|
-
throw new NotFoundException(
|
|
23
|
-
`Error on BbsArticleProvider.index(): unable to find the matched section "${section}".`,
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
/* disable-eslint */
|
|
27
|
-
let articles: IBbsArticle[] = Array.from(dict.values()).map(
|
|
28
|
-
(rec) => rec.article,
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
// SEARCH
|
|
32
|
-
if (input.search !== undefined) {
|
|
33
|
-
if (input.search.writer)
|
|
34
|
-
articles = articles.filter(
|
|
35
|
-
(x) => x.writer.indexOf(input.search!.writer!) !== -1,
|
|
36
|
-
);
|
|
37
|
-
if (input.search.title)
|
|
38
|
-
articles = articles.filter(
|
|
39
|
-
(x) =>
|
|
40
|
-
x.contents
|
|
41
|
-
.at(-1)!
|
|
42
|
-
.title.indexOf(input.search!.title!) !== -1,
|
|
43
|
-
);
|
|
44
|
-
if (input.search.body)
|
|
45
|
-
articles = articles.filter(
|
|
46
|
-
(x) =>
|
|
47
|
-
x.contents.at(-1)!.body.indexOf(input.search!.body!) !==
|
|
48
|
-
-1,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// SORT
|
|
53
|
-
if (input.sort?.length) {
|
|
54
|
-
for (const comp of input.sort.reverse())
|
|
55
|
-
articles = articles.sort((x, y) => {
|
|
56
|
-
const sign = comp[0];
|
|
57
|
-
const column = comp.substring(1);
|
|
58
|
-
const closure = () => {
|
|
59
|
-
if (column === "created_at")
|
|
60
|
-
return (
|
|
61
|
-
new Date(x.created_at).getTime() -
|
|
62
|
-
new Date(y.created_at).getTime()
|
|
63
|
-
);
|
|
64
|
-
else if (column === "updated_at")
|
|
65
|
-
return (
|
|
66
|
-
new Date(
|
|
67
|
-
x.contents.at(-1)!.created_at,
|
|
68
|
-
).getTime() -
|
|
69
|
-
new Date(
|
|
70
|
-
y.contents.at(-1)!.created_at,
|
|
71
|
-
).getTime()
|
|
72
|
-
);
|
|
73
|
-
else if (column === "writer")
|
|
74
|
-
return x.writer.localeCompare(y.writer);
|
|
75
|
-
else
|
|
76
|
-
return x.contents
|
|
77
|
-
.at(-1)!
|
|
78
|
-
.title.localeCompare(y.contents.at(-1)!.title);
|
|
79
|
-
};
|
|
80
|
-
return sign === "+" ? closure() : -closure();
|
|
81
|
-
});
|
|
82
|
-
} else {
|
|
83
|
-
articles.sort(
|
|
84
|
-
(a, b) =>
|
|
85
|
-
new Date(b.created_at).getTime() -
|
|
86
|
-
new Date(a.created_at).getTime(),
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// PAGINATION
|
|
91
|
-
const limit: number = input.limit || 100;
|
|
92
|
-
const start: number = ((input.page || 1) - 1) * limit;
|
|
93
|
-
return {
|
|
94
|
-
pagination: {
|
|
95
|
-
page: input.page || 1,
|
|
96
|
-
limit: limit,
|
|
97
|
-
total_count: articles.length,
|
|
98
|
-
total_pages: Math.ceil(articles.length / limit),
|
|
99
|
-
},
|
|
100
|
-
data: articles.slice(start, start + limit).map((article) => ({
|
|
101
|
-
id: article.id,
|
|
102
|
-
section: article.section,
|
|
103
|
-
writer: article.writer,
|
|
104
|
-
title: article.contents.at(-1)!.title,
|
|
105
|
-
created_at: article.created_at,
|
|
106
|
-
updated_at: article.contents.at(-1)!.created_at,
|
|
107
|
-
})),
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function find(
|
|
112
|
-
section: string,
|
|
113
|
-
id: string,
|
|
114
|
-
password?: string,
|
|
115
|
-
): Promise<IBbsArticle> {
|
|
116
|
-
const dict = storage.get(section);
|
|
117
|
-
if (dict === undefined)
|
|
118
|
-
throw new NotFoundException(
|
|
119
|
-
`Error on BbsArticleProvider.find(): unable to find the matched section "${section}".`,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const record = dict.get(id);
|
|
123
|
-
if (record === undefined)
|
|
124
|
-
throw new NotFoundException(
|
|
125
|
-
`Error on BbsArticleProvider.find(): unable to find the matched article "${id}".`,
|
|
126
|
-
);
|
|
127
|
-
else if (password !== undefined && password !== record.password)
|
|
128
|
-
throw new ForbiddenException(
|
|
129
|
-
`Error on BbsArticleProvider.find(): different password.`,
|
|
130
|
-
);
|
|
131
|
-
return record.article;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export async function store(
|
|
135
|
-
section: string,
|
|
136
|
-
input: IBbsArticle.IStore,
|
|
137
|
-
): Promise<IBbsArticle> {
|
|
138
|
-
const now: string = datetime_to_string(new Date());
|
|
139
|
-
const article: IBbsArticle = {
|
|
140
|
-
id: v4(),
|
|
141
|
-
section,
|
|
142
|
-
writer: input.writer,
|
|
143
|
-
contents: [
|
|
144
|
-
{
|
|
145
|
-
...{
|
|
146
|
-
...input,
|
|
147
|
-
password: undefined,
|
|
148
|
-
},
|
|
149
|
-
id: v4(),
|
|
150
|
-
created_at: now,
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
|
-
created_at: now,
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const dict: Map<string, IRecord> = MapUtil.take(
|
|
157
|
-
storage,
|
|
158
|
-
section,
|
|
159
|
-
() => new Map(),
|
|
160
|
-
);
|
|
161
|
-
dict.set(article.id, {
|
|
162
|
-
article,
|
|
163
|
-
password: input.password,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
return article;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export async function update(
|
|
170
|
-
section: string,
|
|
171
|
-
id: string,
|
|
172
|
-
input: IBbsArticle.IUpdate,
|
|
173
|
-
): Promise<IBbsArticle.IContent> {
|
|
174
|
-
const article: IBbsArticle = await find(section, id, input.password);
|
|
175
|
-
const content: IBbsArticle.IContent = {
|
|
176
|
-
...{
|
|
177
|
-
...input,
|
|
178
|
-
password: undefined,
|
|
179
|
-
},
|
|
180
|
-
id: v4(),
|
|
181
|
-
created_at: datetime_to_string(new Date()),
|
|
182
|
-
};
|
|
183
|
-
article.contents.push(content);
|
|
184
|
-
return content;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
interface IRecord {
|
|
189
|
-
article: IBbsArticle;
|
|
190
|
-
password: string;
|
|
191
|
-
}
|
|
192
|
-
const storage: Map<string, Map<string, IRecord>> = new Map();
|
|
193
|
-
|
|
194
|
-
function datetime_to_string(date: Date): string {
|
|
195
|
-
return (
|
|
196
|
-
cipher(4)(date.getFullYear()) +
|
|
197
|
-
"-" +
|
|
198
|
-
([
|
|
199
|
-
[date.getMonth() + 1, date.getDate()].map(cipher(2)).join("-"),
|
|
200
|
-
[date.getHours(), date.getMinutes(), date.getSeconds()]
|
|
201
|
-
.map(cipher(2))
|
|
202
|
-
.join(":"),
|
|
203
|
-
].join(" ") +
|
|
204
|
-
"." +
|
|
205
|
-
precision(date.getMilliseconds()))
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const cipher = (digit: number) => (value: number) => {
|
|
210
|
-
const str: string = String(value);
|
|
211
|
-
return "0".repeat(digit - str.length) + str;
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const precision = (value: number) => {
|
|
215
|
-
const str: string = String(value);
|
|
216
|
-
return str + "0".repeat(3 - str.length);
|
|
217
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import TSON from "typescript-json";
|
|
2
|
-
import { v4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
import api from "@ORGANIZATION/PROJECT-api/lib/index";
|
|
5
|
-
import { IBbsArticle } from "@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle";
|
|
6
|
-
|
|
7
|
-
import { exception_must_be_thrown } from "../../../internal/exception_must_be_thrown";
|
|
8
|
-
|
|
9
|
-
export async function test_api_bbs_article_at(
|
|
10
|
-
connection: api.IConnection,
|
|
11
|
-
): Promise<void> {
|
|
12
|
-
// STORE A NEW ARTICLE
|
|
13
|
-
const stored: IBbsArticle = await api.functional.bbs.articles.store(
|
|
14
|
-
connection,
|
|
15
|
-
"general",
|
|
16
|
-
{
|
|
17
|
-
writer: "Robot",
|
|
18
|
-
title: "Hello, world!",
|
|
19
|
-
body: "Hello, I'm test automation robot",
|
|
20
|
-
format: "txt",
|
|
21
|
-
files: [
|
|
22
|
-
{
|
|
23
|
-
name: "logo",
|
|
24
|
-
extension: "png",
|
|
25
|
-
url: "https://somewhere.com/logo.png",
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
password: "1234",
|
|
29
|
-
},
|
|
30
|
-
);
|
|
31
|
-
TSON.assertEquals(stored);
|
|
32
|
-
|
|
33
|
-
// READ IT
|
|
34
|
-
const read: IBbsArticle = await api.functional.bbs.articles.at(
|
|
35
|
-
connection,
|
|
36
|
-
stored.section,
|
|
37
|
-
stored.id,
|
|
38
|
-
);
|
|
39
|
-
TSON.assertEquals(read);
|
|
40
|
-
|
|
41
|
-
// CHECK EQUALITY
|
|
42
|
-
if (api.Primitive.equal_to(stored, read) === false)
|
|
43
|
-
throw new Error("Bug on BbsArticleProvider.at(): different data.");
|
|
44
|
-
|
|
45
|
-
// TRY 404 ERRORS
|
|
46
|
-
await exception_must_be_thrown("wrong section", () =>
|
|
47
|
-
api.functional.bbs.articles.at(connection, v4(), stored.id),
|
|
48
|
-
);
|
|
49
|
-
await exception_must_be_thrown("wrong id", () =>
|
|
50
|
-
api.functional.bbs.articles.at(connection, stored.section, v4()),
|
|
51
|
-
);
|
|
52
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import TSON from "typescript-json";
|
|
2
|
-
|
|
3
|
-
import api from "@ORGANIZATION/PROJECT-api/lib/index";
|
|
4
|
-
import { IBbsArticle } from "@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle";
|
|
5
|
-
import { IPage } from "@ORGANIZATION/PROJECT-api/lib/structures/common/IPage";
|
|
6
|
-
|
|
7
|
-
import { ArrayUtil } from "../../../../utils/ArrayUtil";
|
|
8
|
-
import { RandomGenerator } from "../../../internal/RandomGenerator";
|
|
9
|
-
import { validate_index } from "../../../internal/validate_index";
|
|
10
|
-
|
|
11
|
-
export async function test_api_bbs_article_index_search(
|
|
12
|
-
connection: api.IConnection,
|
|
13
|
-
): Promise<void> {
|
|
14
|
-
// GENERATE 100 ARTICLES
|
|
15
|
-
const section: string = "general";
|
|
16
|
-
const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100, () =>
|
|
17
|
-
api.functional.bbs.articles.store(connection, section, {
|
|
18
|
-
writer: RandomGenerator.name(),
|
|
19
|
-
title: RandomGenerator.paragraph(),
|
|
20
|
-
body: RandomGenerator.content(),
|
|
21
|
-
format: "txt",
|
|
22
|
-
files: [],
|
|
23
|
-
password: RandomGenerator.alphabets(8),
|
|
24
|
-
}),
|
|
25
|
-
);
|
|
26
|
-
TSON.assertEquals(articles);
|
|
27
|
-
|
|
28
|
-
// DO SEARCH
|
|
29
|
-
const validator = search(connection, articles);
|
|
30
|
-
await validator(
|
|
31
|
-
"writer",
|
|
32
|
-
(x) => x.writer,
|
|
33
|
-
(elem, word) => elem.writer.indexOf(word) !== -1,
|
|
34
|
-
);
|
|
35
|
-
await validator(
|
|
36
|
-
"title",
|
|
37
|
-
(x) => x.contents.at(-1)!.title,
|
|
38
|
-
(elem, word) => elem.contents.at(-1)!.title.indexOf(word) !== -1,
|
|
39
|
-
);
|
|
40
|
-
await validator(
|
|
41
|
-
"body",
|
|
42
|
-
(x) => x.contents.at(-1)!.body,
|
|
43
|
-
(elem, word) => elem.contents.at(-1)!.body.indexOf(word) !== -1,
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const search =
|
|
48
|
-
(connection: api.IConnection, total: IBbsArticle[]) =>
|
|
49
|
-
async (
|
|
50
|
-
field: string,
|
|
51
|
-
getter: (
|
|
52
|
-
record: IBbsArticle,
|
|
53
|
-
input: IBbsArticle.IRequest.ISearch,
|
|
54
|
-
) => string,
|
|
55
|
-
filter: (record: IBbsArticle, word: string) => boolean,
|
|
56
|
-
): Promise<void> => {
|
|
57
|
-
const input: IBbsArticle.IRequest = {
|
|
58
|
-
limit: total.length,
|
|
59
|
-
search: {},
|
|
60
|
-
};
|
|
61
|
-
const value: string = getter(
|
|
62
|
-
RandomGenerator.pick(total),
|
|
63
|
-
input.search!,
|
|
64
|
-
);
|
|
65
|
-
const matched: IBbsArticle[] = total.filter((elem) =>
|
|
66
|
-
filter(elem, value),
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const page: IPage<IBbsArticle.ISummary> =
|
|
70
|
-
await api.functional.bbs.articles.index(
|
|
71
|
-
connection,
|
|
72
|
-
"general",
|
|
73
|
-
input,
|
|
74
|
-
);
|
|
75
|
-
TSON.assertEquals(page);
|
|
76
|
-
|
|
77
|
-
validate_index(
|
|
78
|
-
`BbsArticleProvider.index() with ${field} searching`,
|
|
79
|
-
matched,
|
|
80
|
-
page.data,
|
|
81
|
-
);
|
|
82
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import TSON from "typescript-json";
|
|
2
|
-
|
|
3
|
-
import api from "@ORGANIZATION/PROJECT-api/lib/index";
|
|
4
|
-
import { IBbsArticle } from "@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle";
|
|
5
|
-
|
|
6
|
-
import { ArrayUtil } from "../../../../utils/ArrayUtil";
|
|
7
|
-
import { GaffComparator } from "../../../internal/GaffComparator";
|
|
8
|
-
import { RandomGenerator } from "../../../internal/RandomGenerator";
|
|
9
|
-
import { validate_index_sort } from "../../../internal/validate_index_sort";
|
|
10
|
-
|
|
11
|
-
export async function test_api_bbs_article_index_sort(
|
|
12
|
-
connection: api.IConnection,
|
|
13
|
-
): Promise<void> {
|
|
14
|
-
// GENERATE 100 ARTICLES
|
|
15
|
-
const section: string = "general";
|
|
16
|
-
const articles: IBbsArticle[] = await ArrayUtil.asyncRepeat(100, () =>
|
|
17
|
-
api.functional.bbs.articles.store(connection, section, {
|
|
18
|
-
writer: RandomGenerator.name(),
|
|
19
|
-
title: RandomGenerator.paragraph(),
|
|
20
|
-
body: RandomGenerator.content(),
|
|
21
|
-
format: "txt",
|
|
22
|
-
files: [],
|
|
23
|
-
password: RandomGenerator.alphabets(8),
|
|
24
|
-
}),
|
|
25
|
-
);
|
|
26
|
-
TSON.assertEquals(articles);
|
|
27
|
-
|
|
28
|
-
// PREPARE VALIDATOR
|
|
29
|
-
const validator = validate_index_sort("BbsArticleProvider.idex()")(
|
|
30
|
-
(input: IBbsArticle.IRequest) =>
|
|
31
|
-
api.functional.bbs.articles.index(connection, section, input),
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// DO VALIDATE
|
|
35
|
-
const components = [
|
|
36
|
-
validator("created_at")(GaffComparator.dates((x) => x.created_at)),
|
|
37
|
-
validator("updated_at")(GaffComparator.dates((x) => x.updated_at)),
|
|
38
|
-
validator("title")(GaffComparator.strings((x) => x.title)),
|
|
39
|
-
validator("writer")(GaffComparator.strings((x) => x.writer)),
|
|
40
|
-
validator(
|
|
41
|
-
"writer",
|
|
42
|
-
"title",
|
|
43
|
-
)(GaffComparator.strings((x) => [x.writer, x.title])),
|
|
44
|
-
];
|
|
45
|
-
for (const comp of components) {
|
|
46
|
-
await comp("+");
|
|
47
|
-
await comp("-");
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import TSON from "typescript-json";
|
|
2
|
-
import { v4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
import api from "@ORGANIZATION/PROJECT-api/lib/index";
|
|
5
|
-
import { IBbsArticle } from "@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle";
|
|
6
|
-
|
|
7
|
-
import { RandomGenerator } from "../../../internal/RandomGenerator";
|
|
8
|
-
|
|
9
|
-
export async function test_api_bbs_article_store(
|
|
10
|
-
connection: api.IConnection,
|
|
11
|
-
): Promise<void> {
|
|
12
|
-
// STORE A NEW ARTICLE
|
|
13
|
-
const stored: IBbsArticle = await api.functional.bbs.articles.store(
|
|
14
|
-
connection,
|
|
15
|
-
"general",
|
|
16
|
-
{
|
|
17
|
-
writer: RandomGenerator.name(),
|
|
18
|
-
title: RandomGenerator.paragraph(),
|
|
19
|
-
body: RandomGenerator.content(),
|
|
20
|
-
format: "txt",
|
|
21
|
-
files: [
|
|
22
|
-
{
|
|
23
|
-
name: "logo",
|
|
24
|
-
extension: "png",
|
|
25
|
-
url: "https://somewhere.com/logo.png",
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
password: v4(),
|
|
29
|
-
},
|
|
30
|
-
);
|
|
31
|
-
TSON.assertEquals(stored);
|
|
32
|
-
|
|
33
|
-
// READ THE DATA AGAIN
|
|
34
|
-
const read: IBbsArticle = await api.functional.bbs.articles.at(
|
|
35
|
-
connection,
|
|
36
|
-
stored.section,
|
|
37
|
-
stored.id,
|
|
38
|
-
);
|
|
39
|
-
TSON.assertEquals(read);
|
|
40
|
-
|
|
41
|
-
// CHECK EXISTENCE
|
|
42
|
-
if (read.id !== stored.id)
|
|
43
|
-
throw new Error("Bug on BbsArticleProvider.store(): failed to store.");
|
|
44
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import TSON from "typescript-json";
|
|
2
|
-
import { v4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
import api from "@ORGANIZATION/PROJECT-api/lib/index";
|
|
5
|
-
import { IBbsArticle } from "@ORGANIZATION/PROJECT-api/lib/structures/bbs/IBbsArticle";
|
|
6
|
-
|
|
7
|
-
import { RandomGenerator } from "../../../internal/RandomGenerator";
|
|
8
|
-
import { exception_must_be_thrown } from "../../../internal/exception_must_be_thrown";
|
|
9
|
-
|
|
10
|
-
export async function test_api_bbs_article_update(
|
|
11
|
-
connection: api.IConnection,
|
|
12
|
-
): Promise<void> {
|
|
13
|
-
// STORE A NEW ARTICLE
|
|
14
|
-
const password: string = v4();
|
|
15
|
-
const article: IBbsArticle = await api.functional.bbs.articles.store(
|
|
16
|
-
connection,
|
|
17
|
-
"general",
|
|
18
|
-
{
|
|
19
|
-
writer: RandomGenerator.name(),
|
|
20
|
-
title: RandomGenerator.paragraph(),
|
|
21
|
-
body: RandomGenerator.content(),
|
|
22
|
-
format: "txt",
|
|
23
|
-
files: [
|
|
24
|
-
{
|
|
25
|
-
name: "logo",
|
|
26
|
-
extension: "png",
|
|
27
|
-
url: "https://somewhere.com/logo.png",
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
password,
|
|
31
|
-
},
|
|
32
|
-
);
|
|
33
|
-
TSON.assertEquals(article);
|
|
34
|
-
|
|
35
|
-
// UPDATE WITH EXACT PASSWORD
|
|
36
|
-
const content: IBbsArticle.IContent =
|
|
37
|
-
await api.functional.bbs.articles.update(
|
|
38
|
-
connection,
|
|
39
|
-
article.section,
|
|
40
|
-
article.id,
|
|
41
|
-
{
|
|
42
|
-
title: RandomGenerator.paragraph(),
|
|
43
|
-
body: RandomGenerator.content(),
|
|
44
|
-
format: "txt",
|
|
45
|
-
files: [],
|
|
46
|
-
password,
|
|
47
|
-
},
|
|
48
|
-
);
|
|
49
|
-
article.contents.push(TSON.assertEquals(content));
|
|
50
|
-
|
|
51
|
-
// TRY UPDATE WITH WRONG PASSWORD
|
|
52
|
-
await exception_must_be_thrown("update with wrong password", () =>
|
|
53
|
-
api.functional.bbs.articles.update(
|
|
54
|
-
connection,
|
|
55
|
-
article.section,
|
|
56
|
-
article.id,
|
|
57
|
-
{
|
|
58
|
-
title: RandomGenerator.paragraph(),
|
|
59
|
-
body: RandomGenerator.content(),
|
|
60
|
-
format: "txt",
|
|
61
|
-
files: [],
|
|
62
|
-
password: v4(),
|
|
63
|
-
},
|
|
64
|
-
),
|
|
65
|
-
);
|
|
66
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { sleep_for } from "tstl/thread/global";
|
|
2
|
-
|
|
3
|
-
import api from "@ORGANIZATION/PROJECT-api";
|
|
4
|
-
|
|
5
|
-
import { Backend } from "../Backend";
|
|
6
|
-
import { Configuration } from "../Configuration";
|
|
7
|
-
import { DynamicImportIterator } from "./internal/DynamicImportIterator";
|
|
8
|
-
|
|
9
|
-
async function main(): Promise<void> {
|
|
10
|
-
// BACKEND SERVER
|
|
11
|
-
const backend: Backend = new Backend();
|
|
12
|
-
await backend.open();
|
|
13
|
-
|
|
14
|
-
//----
|
|
15
|
-
// CLINET CONNECTOR
|
|
16
|
-
//----
|
|
17
|
-
// DO TEST
|
|
18
|
-
const connection: api.IConnection = {
|
|
19
|
-
host: `http://127.0.0.1:${await Configuration.API_PORT()}`,
|
|
20
|
-
encryption: await Configuration.ENCRYPTION_PASSWORD(),
|
|
21
|
-
};
|
|
22
|
-
const exceptions: Error[] = await DynamicImportIterator.force(
|
|
23
|
-
__dirname + "/features",
|
|
24
|
-
{
|
|
25
|
-
prefix: "test",
|
|
26
|
-
parameters: () => [connection],
|
|
27
|
-
},
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
// WAIT FOR A WHILE FOR THE EVENTS
|
|
31
|
-
await sleep_for(2500);
|
|
32
|
-
|
|
33
|
-
// TERMINATE
|
|
34
|
-
await backend.close();
|
|
35
|
-
|
|
36
|
-
if (exceptions.length === 0) console.log("Success");
|
|
37
|
-
else {
|
|
38
|
-
for (const exp of exceptions) console.log(exp);
|
|
39
|
-
process.exit(-1);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
main().catch((exp) => {
|
|
43
|
-
console.log(exp);
|
|
44
|
-
process.exit(-1);
|
|
45
|
-
});
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import cli from "cli";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
|
|
5
|
-
import { IPointer } from "./IPointer";
|
|
6
|
-
import { StopWatch } from "./StopWatch";
|
|
7
|
-
|
|
8
|
-
const EXTENSION = __filename.substr(-2);
|
|
9
|
-
if (EXTENSION === "js") require("source-map-support/register");
|
|
10
|
-
|
|
11
|
-
interface ICommand {
|
|
12
|
-
include?: string;
|
|
13
|
-
exclude?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export namespace DynamicImportIterator {
|
|
17
|
-
export type Closure<Arguments extends any[], Ret = any> = (
|
|
18
|
-
...args: Arguments
|
|
19
|
-
) => Promise<Ret>;
|
|
20
|
-
type Module<Arguments extends any[]> = {
|
|
21
|
-
[key: string]: Closure<Arguments>;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export interface IOptions<Parameters extends any[], Ret = any> {
|
|
25
|
-
prefix: string;
|
|
26
|
-
parameters: (name: string) => Parameters;
|
|
27
|
-
wrapper?: (
|
|
28
|
-
name: string,
|
|
29
|
-
closure: Closure<Parameters, Ret>,
|
|
30
|
-
) => Promise<any>;
|
|
31
|
-
counter?: IPointer<number>;
|
|
32
|
-
showElapsedTime?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function main<Arguments extends any[]>(
|
|
36
|
-
path: string,
|
|
37
|
-
options: IOptions<Arguments>,
|
|
38
|
-
): Promise<void> {
|
|
39
|
-
const command: ICommand = cli.parse();
|
|
40
|
-
await iterate(options, command, path);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export async function force<Arguments extends any[]>(
|
|
44
|
-
path: string,
|
|
45
|
-
options: IOptions<Arguments>,
|
|
46
|
-
): Promise<Error[]> {
|
|
47
|
-
const command: ICommand = cli.parse();
|
|
48
|
-
const exceptions: Error[] = [];
|
|
49
|
-
|
|
50
|
-
await iterate(options, command, path, exceptions);
|
|
51
|
-
return exceptions;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function iterate<Arguments extends any[]>(
|
|
55
|
-
options: IOptions<Arguments>,
|
|
56
|
-
command: ICommand,
|
|
57
|
-
path: string,
|
|
58
|
-
exceptions?: Error[],
|
|
59
|
-
): Promise<void> {
|
|
60
|
-
const directory: string[] = await fs.promises.readdir(path);
|
|
61
|
-
for (const file of directory) {
|
|
62
|
-
const current: string = `${path}/${file}`;
|
|
63
|
-
const stats: fs.Stats = await fs.promises.lstat(current);
|
|
64
|
-
|
|
65
|
-
if (stats.isDirectory() === true) {
|
|
66
|
-
await iterate(options, command, current, exceptions);
|
|
67
|
-
continue;
|
|
68
|
-
} else if (file.substr(-3) !== `.${EXTENSION}`) continue;
|
|
69
|
-
|
|
70
|
-
const external: Module<Arguments> = await import(current);
|
|
71
|
-
await execute(options, command, external, exceptions);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function execute<Arguments extends any[]>(
|
|
76
|
-
options: IOptions<Arguments>,
|
|
77
|
-
command: ICommand,
|
|
78
|
-
external: Module<Arguments>,
|
|
79
|
-
exceptions?: Error[],
|
|
80
|
-
): Promise<void> {
|
|
81
|
-
for (const key in external) {
|
|
82
|
-
if (command.exclude && key.indexOf(command.exclude) !== -1)
|
|
83
|
-
continue;
|
|
84
|
-
else if (command.include && key.indexOf(command.include) === -1)
|
|
85
|
-
continue;
|
|
86
|
-
else if (key.substr(0, options.prefix.length) !== options.prefix)
|
|
87
|
-
continue;
|
|
88
|
-
else if (!(external[key] instanceof Function)) continue;
|
|
89
|
-
|
|
90
|
-
const closure: Closure<Arguments> = external[key];
|
|
91
|
-
const func = async () => {
|
|
92
|
-
if (options.wrapper !== undefined)
|
|
93
|
-
await options.wrapper(key, closure);
|
|
94
|
-
else await closure(...options.parameters(key));
|
|
95
|
-
};
|
|
96
|
-
const label: string = chalk.greenBright(key);
|
|
97
|
-
try {
|
|
98
|
-
if (options.counter) ++options.counter.value;
|
|
99
|
-
if (options.showElapsedTime === false) {
|
|
100
|
-
await func();
|
|
101
|
-
console.log(` - ${label}`);
|
|
102
|
-
} else {
|
|
103
|
-
const time: number = await StopWatch.measure(func);
|
|
104
|
-
console.log(
|
|
105
|
-
` - ${label}: ${chalk.yellowBright(
|
|
106
|
-
time.toLocaleString(),
|
|
107
|
-
)} ms`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
} catch (exp) {
|
|
111
|
-
if (!(exp instanceof Error)) return;
|
|
112
|
-
|
|
113
|
-
console.log(` - ${label} -> ${chalk.redBright(exp.name)}`);
|
|
114
|
-
if (exceptions !== undefined) exceptions.push(exp);
|
|
115
|
-
else throw exp;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|