velocious 1.0.1 → 1.0.2
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 +26 -0
- package/bin/velocious.mjs +8 -0
- package/index.mjs +17 -0
- package/package.json +13 -6
- package/peak_flow.yml +9 -5
- package/spec/cli/generate/migration-spec.mjs +9 -0
- package/spec/database/connection/drivers/mysql/{query-parser-spec.cjs → query-parser-spec.mjs} +12 -8
- package/spec/database/drivers/mysql/connection-spec.mjs +21 -0
- package/spec/database/record/create-spec.mjs +14 -0
- package/spec/database/record/destroy-spec.mjs +17 -0
- package/spec/database/record/find-spec.mjs +29 -0
- package/spec/database/record/update-spec.mjs +15 -0
- package/spec/dummy/index.mjs +69 -0
- package/spec/dummy/src/config/database.example.mjs +15 -0
- package/spec/dummy/src/config/database.peakflow.mjs +15 -0
- package/spec/dummy/src/{routes.cjs → config/routes.mjs} +3 -2
- package/spec/dummy/src/database/migrations/001-create-tasks.mjs +12 -0
- package/spec/dummy/src/models/task.mjs +4 -0
- package/spec/dummy/src/routes/tasks/controller.mjs +26 -0
- package/spec/http-server/{client-spec.cjs → client-spec.mjs} +12 -5
- package/spec/http-server/{get-spec.cjs → get-spec.mjs} +2 -2
- package/spec/http-server/post-spec.mjs +72 -0
- package/spec/support/jasmine.json +4 -3
- package/src/application.mjs +56 -0
- package/src/cli/commands/db/create.mjs +14 -0
- package/src/cli/commands/generate/migration.mjs +12 -0
- package/src/cli/index.mjs +39 -0
- package/src/configuration.mjs +27 -0
- package/src/{controller.cjs → controller.mjs} +21 -4
- package/src/database/drivers/base.mjs +9 -0
- package/src/database/drivers/index.mjs +5 -0
- package/src/database/drivers/mysql/connect-connection.mjs +12 -0
- package/src/database/drivers/mysql/index.mjs +77 -0
- package/src/database/drivers/mysql/options.mjs +17 -0
- package/src/database/drivers/mysql/query-parser.mjs +25 -0
- package/src/database/drivers/mysql/query.mjs +26 -0
- package/src/database/drivers/mysql/sql/delete.mjs +19 -0
- package/src/database/drivers/mysql/sql/insert.mjs +29 -0
- package/src/database/drivers/mysql/sql/update.mjs +31 -0
- package/src/database/handler.mjs +11 -0
- package/src/database/index.mjs +15 -0
- package/src/database/migration/index.mjs +5 -0
- package/src/database/migrator/index.mjs +15 -0
- package/src/database/pool/index.mjs +43 -0
- package/src/database/query/delete-base.mjs +15 -0
- package/src/database/query/from-base.mjs +9 -0
- package/src/database/query/from-plain.mjs +12 -0
- package/src/database/query/{from-table.cjs → from-table.mjs} +2 -2
- package/src/database/query/index.mjs +144 -0
- package/src/database/query/insert-base.mjs +15 -0
- package/src/database/query/join-base.mjs +9 -0
- package/src/database/query/join-plain.mjs +12 -0
- package/src/database/query/order-base.mjs +9 -0
- package/src/database/query/order-plain.mjs +21 -0
- package/src/database/query/select-base.mjs +9 -0
- package/src/database/query/select-plain.mjs +12 -0
- package/src/database/query/{select-table-and-column.cjs → select-table-and-column.mjs} +4 -4
- package/src/database/query/update-base.mjs +16 -0
- package/src/database/query-parser/{from-parser.cjs → from-parser.mjs} +3 -6
- package/src/database/query-parser/{joins-parser.cjs → joins-parser.mjs} +3 -6
- package/src/database/query-parser/{options.cjs → options.mjs} +13 -2
- package/src/database/query-parser/{select-parser.cjs → select-parser.mjs} +7 -6
- package/src/database/record/index.mjs +187 -0
- package/src/database/record/record-not-found-error.mjs +1 -0
- package/src/{error-logger.js → error-logger.mjs} +1 -1
- package/src/http-server/client/{index.cjs → index.mjs} +10 -11
- package/src/http-server/client/params-to-object.mjs +68 -0
- package/src/http-server/client/request-buffer/form-data-part.mjs +42 -0
- package/src/http-server/client/request-buffer/header.mjs +7 -0
- package/src/http-server/client/request-buffer/index.mjs +229 -0
- package/src/http-server/client/request-parser.mjs +47 -0
- package/src/http-server/client/{request-runner.cjs → request-runner.mjs} +5 -5
- package/src/http-server/client/request.mjs +15 -0
- package/src/http-server/client/{response.cjs → response.mjs} +1 -1
- package/src/http-server/index.mjs +137 -0
- package/src/http-server/server-client.mjs +47 -0
- package/src/http-server/worker-handler/index.mjs +79 -0
- package/src/http-server/worker-handler/worker-script.mjs +4 -0
- package/src/http-server/worker-handler/{worker-thread.cjs → worker-thread.mjs} +18 -11
- package/src/{logger.cjs → logger.mjs} +2 -2
- package/src/routes/base-route.mjs +34 -0
- package/src/routes/{get-route.cjs → get-route.mjs} +5 -3
- package/src/routes/index.mjs +9 -0
- package/src/routes/{resolver.cjs → resolver.mjs} +15 -9
- package/src/routes/{resource-route.cjs → resource-route.mjs} +9 -5
- package/src/routes/root-route.mjs +6 -0
- package/bin/velocious +0 -14
- package/index.cjs +0 -13
- package/spec/dummy/config/databases.example.json +0 -10
- package/spec/dummy/config/databases.json +0 -0
- package/spec/dummy/config/databases.peakflow.json +0 -11
- package/spec/dummy/index.cjs +0 -40
- package/spec/dummy/src/models/task.cjs +0 -4
- package/spec/dummy/src/routes/tasks/controller.cjs +0 -18
- package/src/application.cjs +0 -36
- package/src/configuration.cjs +0 -14
- package/src/database/connection/drivers/mysql/index.cjs +0 -5
- package/src/database/connection/drivers/mysql/options.cjs +0 -7
- package/src/database/connection/drivers/mysql/query-parser.cjs +0 -26
- package/src/database/connection/index.cjs +0 -2
- package/src/database/handler.cjs +0 -5
- package/src/database/index.cjs +0 -9
- package/src/database/pool/index.cjs +0 -2
- package/src/database/query/from-base.cjs +0 -15
- package/src/database/query/from-plain.cjs +0 -12
- package/src/database/query/index.cjs +0 -59
- package/src/database/query/join-base.cjs +0 -15
- package/src/database/query/join-plain.cjs +0 -12
- package/src/database/query/select-base.cjs +0 -15
- package/src/database/query/select-plain.cjs +0 -12
- package/src/database/record/index.cjs +0 -5
- package/src/http-server/client/request-parser.cjs +0 -92
- package/src/http-server/client/request.cjs +0 -25
- package/src/http-server/index.cjs +0 -78
- package/src/http-server/worker-handler/index.cjs +0 -78
- package/src/http-server/worker-handler/socket-handler.cjs +0 -35
- package/src/http-server/worker-handler/worker-script.cjs +0 -4
- package/src/routes/base-route.cjs +0 -25
- package/src/routes/index.cjs +0 -9
- package/src/routes/root-route.cjs +0 -4
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# README
|
|
2
|
+
|
|
3
|
+
This is still work in progress.
|
|
4
|
+
|
|
5
|
+
* Concurrent multi threadded web server
|
|
6
|
+
* Database framework ala Rails
|
|
7
|
+
* Database models ala Rails
|
|
8
|
+
* Database models that work almost the same in frontend and backend
|
|
9
|
+
* Migrations ala Rails
|
|
10
|
+
* Controllers and views ala Rails
|
|
11
|
+
|
|
12
|
+
## Migrations
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx velocious db:g:migration create_tasks
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx velocious db:migrate
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Testing
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm test
|
|
26
|
+
```
|
package/index.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Application from "./src/application.mjs"
|
|
2
|
+
import Cli from "./src/cli/index.mjs"
|
|
3
|
+
import Controller from "./src/controller.mjs"
|
|
4
|
+
import Database from "./src/database/index.mjs"
|
|
5
|
+
import DatabasePool from "./src/database/pool/index.mjs"
|
|
6
|
+
import HttpServer from "./src/http-server/index.mjs"
|
|
7
|
+
import Routes from "./src/routes/index.mjs"
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
Application,
|
|
11
|
+
Cli,
|
|
12
|
+
Controller,
|
|
13
|
+
Database,
|
|
14
|
+
DatabasePool,
|
|
15
|
+
HttpServer,
|
|
16
|
+
Routes
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"bin": {
|
|
3
|
+
"velocious": "bin/velocious.mjs"
|
|
4
|
+
},
|
|
2
5
|
"name": "velocious",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"main": "index.
|
|
6
|
+
"version": "1.0.2",
|
|
7
|
+
"main": "index.mjs",
|
|
5
8
|
"scripts": {
|
|
6
|
-
"test": "jasmine"
|
|
9
|
+
"test": "jasmine",
|
|
10
|
+
"velocious": "asd"
|
|
7
11
|
},
|
|
12
|
+
"type": "module",
|
|
8
13
|
"repository": {
|
|
9
14
|
"type": "git",
|
|
10
15
|
"url": "git+https://github.com/kaspernj/velocious.git"
|
|
@@ -17,13 +22,15 @@
|
|
|
17
22
|
"homepage": "https://github.com/kaspernj/velocious#readme",
|
|
18
23
|
"description": "",
|
|
19
24
|
"devDependencies": {
|
|
20
|
-
"jasmine": "^
|
|
21
|
-
"
|
|
25
|
+
"jasmine": "^5.0.2",
|
|
26
|
+
"mysql": "^2.18.1",
|
|
27
|
+
"node-fetch": "^3.3.1"
|
|
22
28
|
},
|
|
23
29
|
"dependencies": {
|
|
24
30
|
"diggerize": "^1.0.3",
|
|
25
31
|
"ejs": "^3.1.6",
|
|
26
32
|
"escape-string-regexp": "^1.0.5",
|
|
27
|
-
"
|
|
33
|
+
"incorporator": "^1.0.2",
|
|
34
|
+
"inflection": "^2.0.0"
|
|
28
35
|
}
|
|
29
36
|
}
|
package/peak_flow.yml
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
+
before_install:
|
|
2
|
+
- curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
|
|
3
|
+
- sudo apt-get install -y nodejs
|
|
1
4
|
before_script:
|
|
2
|
-
- cp spec/dummy/config/
|
|
5
|
+
- cp spec/dummy/src/config/database.peakflow.mjs spec/dummy/src/config/database.mjs
|
|
3
6
|
- npm install
|
|
4
|
-
- wait-for-it
|
|
7
|
+
- wait-for-it mariadb:3306
|
|
5
8
|
services:
|
|
6
|
-
|
|
9
|
+
mariadb:
|
|
7
10
|
environment:
|
|
8
11
|
MYSQL_USER: peakflow
|
|
9
12
|
MYSQL_PASSWORD: password
|
|
10
13
|
MYSQL_ROOT_PASSWORD: password
|
|
11
14
|
MYSQL_DATABASE: velocious_test
|
|
12
|
-
image:
|
|
15
|
+
image: mariadb:latest
|
|
13
16
|
expose:
|
|
14
17
|
- 3306
|
|
15
|
-
mem_limit:
|
|
18
|
+
mem_limit: 4096m
|
|
19
|
+
restart_policy: on-failure
|
|
16
20
|
script:
|
|
17
21
|
- npm test
|
package/spec/database/connection/drivers/mysql/{query-parser-spec.cjs → query-parser-spec.mjs}
RENAMED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import Database from "../../../../../src/database/index.mjs"
|
|
2
|
+
import MysqlQueryParser from "../../../../../src/database/drivers/mysql/query-parser.mjs"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import FromTable from "../../../../../src/database/query/from-table.mjs"
|
|
5
|
+
import SelectTableAndColumn from "../../../../../src/database/query/select-table-and-column.mjs"
|
|
6
|
+
|
|
7
|
+
import MysqlDriverClass from "../../../../../src/database/drivers/mysql/index.mjs"
|
|
8
|
+
|
|
9
|
+
const mysqlDriver = new MysqlDriverClass()
|
|
6
10
|
|
|
7
11
|
describe("database - connection - drivers - mysql - query parser", () => {
|
|
8
12
|
it("generates sql with selects, joins and orders", () => {
|
|
9
|
-
const handler = new Handler()
|
|
10
|
-
const query = new Query({handler})
|
|
13
|
+
const handler = new Database.Handler()
|
|
14
|
+
const query = new Database.Query({driver: mysqlDriver, handler})
|
|
11
15
|
.select(["tasks.id", "tasks.name"])
|
|
12
16
|
.from("tasks")
|
|
13
17
|
.joins("LEFT JOIN projects ON projects.id = tasks.project_id")
|
|
@@ -18,8 +22,8 @@ describe("database - connection - drivers - mysql - query parser", () => {
|
|
|
18
22
|
})
|
|
19
23
|
|
|
20
24
|
it("generates sql with selects, joins and orders", () => {
|
|
21
|
-
const handler = new Handler()
|
|
22
|
-
const query = new Query({handler})
|
|
25
|
+
const handler = new Database.Handler()
|
|
26
|
+
const query = new Database.Query({driver: mysqlDriver, handler})
|
|
23
27
|
.select([
|
|
24
28
|
new SelectTableAndColumn({tableName: "tasks", columnName: "id"}),
|
|
25
29
|
new SelectTableAndColumn({tableName: "tasks", columnName: "name"})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Database from "../../../../src/database/index.mjs"
|
|
2
|
+
import {databaseConfiguration} from "../../../dummy/src/config/database.mjs"
|
|
3
|
+
import {digg} from "diggerize"
|
|
4
|
+
|
|
5
|
+
const mysqlConfig = digg(databaseConfiguration(), "default", "master")
|
|
6
|
+
const Mysql = Database.Drivers.Mysql
|
|
7
|
+
|
|
8
|
+
describe("Database - Drivers - Mysql - Connection", () => {
|
|
9
|
+
it("connects", async () => {
|
|
10
|
+
const mysql = new Mysql(mysqlConfig)
|
|
11
|
+
|
|
12
|
+
await mysql.connect()
|
|
13
|
+
|
|
14
|
+
const result = await mysql.query("SELECT \"1\" AS test1, \"2\" AS test2")
|
|
15
|
+
|
|
16
|
+
expect(result).toEqual([{
|
|
17
|
+
test1: "1",
|
|
18
|
+
test2: "2"
|
|
19
|
+
}])
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Dummy from "../../dummy/index.mjs"
|
|
2
|
+
import Task from "../../dummy/src/models/task.mjs"
|
|
3
|
+
|
|
4
|
+
describe("Record - create", () => {
|
|
5
|
+
it("creates a new simple record", async () => {
|
|
6
|
+
await Dummy.run(async () => {
|
|
7
|
+
const task = new Task({name: "Test task"})
|
|
8
|
+
|
|
9
|
+
await task.save()
|
|
10
|
+
|
|
11
|
+
expect(task.id()).not.toBeUndefined()
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Dummy from "../../dummy/index.mjs"
|
|
2
|
+
import Task from "../../dummy/src/models/task.mjs"
|
|
3
|
+
|
|
4
|
+
describe("Record - destroy", () => {
|
|
5
|
+
it("destroys a record", async () => {
|
|
6
|
+
await Dummy.run(async () => {
|
|
7
|
+
const task = new Task({name: "Test task"})
|
|
8
|
+
|
|
9
|
+
await task.save()
|
|
10
|
+
await task.destroy()
|
|
11
|
+
|
|
12
|
+
const foundTask = await Task.where({id: task.id()}).first()
|
|
13
|
+
|
|
14
|
+
expect(foundTask).toEqual(undefined)
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Dummy from "../../dummy/index.mjs"
|
|
2
|
+
import RecordNotFoundError from "../../../src/database/record/record-not-found-error.mjs"
|
|
3
|
+
import Task from "../../dummy/src/models/task.mjs"
|
|
4
|
+
|
|
5
|
+
describe("Record - find", () => {
|
|
6
|
+
it("finds an existing record", async () => {
|
|
7
|
+
await Dummy.run(async () => {
|
|
8
|
+
const task = new Task({name: "Test task"})
|
|
9
|
+
|
|
10
|
+
await task.save()
|
|
11
|
+
|
|
12
|
+
const foundTask = await Task.find(task.id())
|
|
13
|
+
|
|
14
|
+
expect(foundTask.readAttribute("name")).toEqual("Test task")
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it("raises an error when a record isn't found", async () => {
|
|
19
|
+
await Dummy.run(async () => {
|
|
20
|
+
try {
|
|
21
|
+
await Task.find(123)
|
|
22
|
+
throw new Error("Didn't expect to reach this")
|
|
23
|
+
} catch (error) {
|
|
24
|
+
expect(error.message).toEqual("Couldn't find Task with 'id'=123")
|
|
25
|
+
expect(error.constructor.name).toEqual("RecordNotFoundError")
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Dummy from "../../dummy/index.mjs"
|
|
2
|
+
import Task from "../../dummy/src/models/task.mjs"
|
|
3
|
+
|
|
4
|
+
describe("Record - update", () => {
|
|
5
|
+
it("updates a record", async () => {
|
|
6
|
+
await Dummy.run(async () => {
|
|
7
|
+
const task = new Task({name: "Test task"})
|
|
8
|
+
|
|
9
|
+
await task.save()
|
|
10
|
+
await task.update({name: "Updated name"})
|
|
11
|
+
|
|
12
|
+
expect(task.readAttribute("name")).toEqual("Updated name")
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Application from "../../src/application.mjs"
|
|
2
|
+
import DatabasePool from "../../src/database/pool/index.mjs"
|
|
3
|
+
import {dirname} from "path"
|
|
4
|
+
import {fileURLToPath} from "url"
|
|
5
|
+
|
|
6
|
+
export default class Dummy {
|
|
7
|
+
static current() {
|
|
8
|
+
if (!global.velociousDummy) {
|
|
9
|
+
global.velociousDummy = new Dummy()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return global.velociousDummy
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static async prepare() {
|
|
16
|
+
const connection = DatabasePool.current().singleConnection()
|
|
17
|
+
|
|
18
|
+
await connection.query("DROP TABLE IF EXISTS tasks")
|
|
19
|
+
await connection.query("CREATE TABLE tasks (id MEDIUMINT NOT NULL AUTO_INCREMENT, name VARCHAR(255), description TEXT, PRIMARY KEY (id))")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static async run(callback) {
|
|
23
|
+
await this.current().run(callback)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run(callback) {
|
|
27
|
+
await this.start()
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await Dummy.prepare()
|
|
31
|
+
await callback()
|
|
32
|
+
} finally {
|
|
33
|
+
await this.stop()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async start() {
|
|
38
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
39
|
+
const __dirname = dirname(__filename)
|
|
40
|
+
|
|
41
|
+
this.application = new Application({
|
|
42
|
+
databases: {
|
|
43
|
+
default: {
|
|
44
|
+
host: "mysql",
|
|
45
|
+
username: "user",
|
|
46
|
+
password: ""
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
debug: false,
|
|
50
|
+
directory: __dirname,
|
|
51
|
+
httpServer: {port: 3006}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
await this.application.initialize()
|
|
55
|
+
await this.application.startHttpServer()
|
|
56
|
+
|
|
57
|
+
const databasePool = DatabasePool.current()
|
|
58
|
+
|
|
59
|
+
if (!databasePool.isConnected()) {
|
|
60
|
+
await databasePool.connect()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async stop() {
|
|
65
|
+
if (this.application.isActive()) {
|
|
66
|
+
await this.application.stop()
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const databaseConfiguration = () => {
|
|
2
|
+
return {
|
|
3
|
+
"default": {
|
|
4
|
+
"master": {
|
|
5
|
+
"type": "mysql",
|
|
6
|
+
"host": "mariadb",
|
|
7
|
+
"username": "username",
|
|
8
|
+
"password": "password",
|
|
9
|
+
"database": "velocious_test"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {databaseConfiguration}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const databaseConfiguration = () => {
|
|
2
|
+
return {
|
|
3
|
+
"default": {
|
|
4
|
+
"master": {
|
|
5
|
+
"type": "mysql",
|
|
6
|
+
"host": "mariadb",
|
|
7
|
+
"username": "peakflow",
|
|
8
|
+
"password": "password",
|
|
9
|
+
"database": "velocious_test"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {databaseConfiguration}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import Routes from "../../../../src/routes/index.mjs"
|
|
2
|
+
|
|
2
3
|
const routes = new Routes()
|
|
3
4
|
|
|
4
5
|
routes.draw((route) => {
|
|
@@ -7,4 +8,4 @@ routes.draw((route) => {
|
|
|
7
8
|
})
|
|
8
9
|
})
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
export default {routes}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {Database} from "../../../../index.mjs"
|
|
2
|
+
|
|
3
|
+
export default class CreateTasks extends Database.Migration {
|
|
4
|
+
change() {
|
|
5
|
+
this.createTable("tasks", (table) => {
|
|
6
|
+
table.bigint("id", {autoIncrement: true, primaryKey: true})
|
|
7
|
+
table.string("name")
|
|
8
|
+
table.text("description")
|
|
9
|
+
table.timestamps()
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Controller from "../../../../../src/controller.mjs"
|
|
2
|
+
import {digg} from "diggerize"
|
|
3
|
+
import Task from "../../models/task.mjs"
|
|
4
|
+
|
|
5
|
+
export default class TasksController extends Controller {
|
|
6
|
+
index() {
|
|
7
|
+
this.viewParams.numbers = [1, 2, 3, 4, 5]
|
|
8
|
+
this.render()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async show() {
|
|
12
|
+
const taskId = digg(params, "id")
|
|
13
|
+
const task = await Task.find(taskId)
|
|
14
|
+
|
|
15
|
+
this.viewParams.task = task
|
|
16
|
+
this.render()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async create() {
|
|
20
|
+
const task = new Task(this.params().task)
|
|
21
|
+
|
|
22
|
+
await task.save()
|
|
23
|
+
|
|
24
|
+
this.render({json: {status: "success"}})
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import Client from "../../src/http-server/client/index.mjs"
|
|
2
|
+
import Configuration from "../../src/configuration.mjs"
|
|
3
|
+
import {digg} from "diggerize"
|
|
4
|
+
import {dirname} from "path"
|
|
5
|
+
import {fileURLToPath} from "url"
|
|
6
|
+
import path from "path"
|
|
5
7
|
|
|
6
8
|
describe("http server - client", () => {
|
|
7
|
-
it("spawns a request for each that it is fed", () => {
|
|
9
|
+
it("spawns a request for each that it is fed", async () => {
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
11
|
+
const __dirname = dirname(__filename)
|
|
8
12
|
const dummyDirectory = path.join(__dirname, "../dummy")
|
|
9
13
|
const configuration = new Configuration({
|
|
10
14
|
directory: dummyDirectory
|
|
11
15
|
})
|
|
16
|
+
|
|
17
|
+
await configuration.initialize()
|
|
18
|
+
|
|
12
19
|
const client = new Client({
|
|
13
20
|
clientCount: 0,
|
|
14
21
|
configuration
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fetch from "node-fetch"
|
|
2
|
+
import Dummy from "../dummy/index.mjs"
|
|
3
|
+
import querystring from "querystring"
|
|
4
|
+
import Task from "../dummy/src/models/task.mjs"
|
|
5
|
+
|
|
6
|
+
describe("HttpServer", () => {
|
|
7
|
+
it("handles post requests", async () => {
|
|
8
|
+
await Dummy.run(async () => {
|
|
9
|
+
const postData = querystring.stringify({"task[name]": "Test create task"})
|
|
10
|
+
const response = await fetch(
|
|
11
|
+
"http://localhost:3006/tasks",
|
|
12
|
+
{
|
|
13
|
+
body: postData,
|
|
14
|
+
headers: {
|
|
15
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
16
|
+
"Content-Length": Buffer.byteLength(postData)
|
|
17
|
+
},
|
|
18
|
+
method: "POST"
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
const text = await response.text()
|
|
22
|
+
const createdTask = await Task.last()
|
|
23
|
+
|
|
24
|
+
expect(text).toEqual('{"status":"success"}')
|
|
25
|
+
expect(createdTask.readAttribute("name")).toEqual("Test create task")
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it("handles post json requests", async () => {
|
|
30
|
+
await Dummy.run(async () => {
|
|
31
|
+
const postData = JSON.stringify({task: {name: "Test create task"}})
|
|
32
|
+
const response = await fetch(
|
|
33
|
+
"http://localhost:3006/tasks",
|
|
34
|
+
{
|
|
35
|
+
body: postData,
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
"Content-Length": Buffer.byteLength(postData)
|
|
39
|
+
},
|
|
40
|
+
method: "POST"
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
const text = await response.text()
|
|
44
|
+
const createdTask = await Task.last()
|
|
45
|
+
|
|
46
|
+
expect(text).toEqual('{"status":"success"}')
|
|
47
|
+
expect(createdTask.readAttribute("name")).toEqual("Test create task")
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it("handles post form-data requests", async () => {
|
|
52
|
+
await Dummy.run(async () => {
|
|
53
|
+
const body = new FormData()
|
|
54
|
+
|
|
55
|
+
body.append("task[name]", "Test create task")
|
|
56
|
+
body.append("task[description]", "This is a task")
|
|
57
|
+
|
|
58
|
+
const response = await fetch(
|
|
59
|
+
"http://localhost:3006/tasks",
|
|
60
|
+
{
|
|
61
|
+
body,
|
|
62
|
+
method: "POST"
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
const text = await response.text()
|
|
66
|
+
const createdTask = await Task.last()
|
|
67
|
+
|
|
68
|
+
expect(text).toEqual('{"status":"success"}')
|
|
69
|
+
expect(createdTask.readAttribute("name")).toEqual("Test create task")
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"spec_dir": "spec",
|
|
3
3
|
"spec_files": [
|
|
4
|
-
"
|
|
4
|
+
"**/*-spec.mjs"
|
|
5
5
|
],
|
|
6
6
|
"helpers": [
|
|
7
|
-
"helpers/**/*.
|
|
7
|
+
"helpers/**/*.mjs"
|
|
8
8
|
],
|
|
9
9
|
"stopSpecOnExpectationFailure": false,
|
|
10
|
-
"random": false
|
|
10
|
+
"random": false,
|
|
11
|
+
"jsLoader": "import"
|
|
11
12
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {digs} from "diggerize"
|
|
2
|
+
import Configuration from "./configuration.mjs"
|
|
3
|
+
import logger from "./logger.mjs"
|
|
4
|
+
import HttpServer from "./http-server/index.mjs"
|
|
5
|
+
|
|
6
|
+
export default class VelociousApplication {
|
|
7
|
+
constructor({debug, directory, httpServer}) {
|
|
8
|
+
this.configuration = new Configuration({debug, directory})
|
|
9
|
+
this.httpServerConfiguration = httpServer ?? {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async initialize() {
|
|
13
|
+
if (global.velociousApplication) throw new Error("A Velocious application is already running")
|
|
14
|
+
if (global.velociousConfiguration) throw new Error("A Velocious configuration has already been set")
|
|
15
|
+
|
|
16
|
+
global.velociousApplication = this
|
|
17
|
+
global.velociousConfiguration = this.configuration
|
|
18
|
+
|
|
19
|
+
await this.configuration.initialize()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
isActive() {
|
|
23
|
+
return this.httpServer?.isActive()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async run(callback) {
|
|
27
|
+
await this.start()
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await callback()
|
|
31
|
+
} finally {
|
|
32
|
+
this.stop()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async startHttpServer() {
|
|
37
|
+
const {configuration, httpServerConfiguration} = digs(this, "configuration", "httpServerConfiguration")
|
|
38
|
+
|
|
39
|
+
const port = httpServerConfiguration.port || 3006
|
|
40
|
+
|
|
41
|
+
logger(this, `Starting server on port ${port}`)
|
|
42
|
+
|
|
43
|
+
this.httpServer = new HttpServer({configuration, port})
|
|
44
|
+
|
|
45
|
+
await this.httpServer.start()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async stop() {
|
|
49
|
+
logger(this, "Stopping server")
|
|
50
|
+
|
|
51
|
+
await this.httpServer.stop()
|
|
52
|
+
|
|
53
|
+
global.velociousApplication = undefined
|
|
54
|
+
global.velociousConfiguration = undefined
|
|
55
|
+
}
|
|
56
|
+
}
|