Skip to content

Commit ad0f021

Browse files
authored
Release v3.0.0-alpha.4 (#29)
* feat(lib/nameserver): added, with tests * feat(routes/nameserver): added, with tests * group: updated validate names (_res, _req) * update validate version
1 parent 28f979e commit ad0f021

19 files changed

+588
-90
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
### Unreleased
44

5+
6+
### [3.0.0-alpha.4] - 2024-03-05
7+
8+
- feat(lib/nameserver): added, with tests
9+
- feat(routes/nameserver): added, with tests
10+
511
### 3.0.0-alpha.3
612

713
- routes/permission: added GET, POST, DELETE
@@ -14,3 +20,4 @@
1420

1521

1622
[3.0.0-alpha.3]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.3
23+
[3.0.0-alpha.4]: https://github.com/NicTool/api/releases/tag/3.0.0-alpha.4

lib/nameserver.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import Mysql from './mysql.js'
2+
import { mapToDbColumn } from './util.js'
3+
4+
const nsDbMap = { id: 'nt_nameserver_id', gid: 'nt_group_id' }
5+
const boolFields = ['deleted', 'export_serials']
6+
7+
class Nameserver {
8+
constructor() {
9+
this.mysql = Mysql
10+
}
11+
12+
async create(args) {
13+
if (args.id) {
14+
const g = await this.get({ id: args.id })
15+
if (g.length === 1) return g[0].id
16+
}
17+
18+
if (args.export.type) {
19+
args = JSON.parse(JSON.stringify(args))
20+
const rows = await Mysql.execute(
21+
...Mysql.select('SELECT id FROM nt_nameserver_export_type', {
22+
name: args.export.type,
23+
}),
24+
)
25+
args.export_type_id = rows[0].id
26+
delete args.export.type
27+
}
28+
29+
return await Mysql.execute(
30+
...Mysql.insert(
31+
`nt_nameserver`,
32+
mapToDbColumn(objectToDb(args), nsDbMap),
33+
),
34+
)
35+
}
36+
37+
async get(args) {
38+
if (args.name !== undefined) {
39+
args['ns.name'] = args.name
40+
delete args.name
41+
}
42+
const rows = await Mysql.execute(
43+
...Mysql.select(
44+
`SELECT ns.nt_nameserver_id AS id
45+
, ns.nt_group_id AS gid
46+
, ns.name
47+
, ns.ttl
48+
, ns.description
49+
, ns.address
50+
, ns.address6
51+
, ns.remote_login
52+
, ns.logdir
53+
, ns.datadir
54+
, ns.export_interval
55+
, ns.export_serials
56+
, ns.export_status
57+
, ns.deleted
58+
, t.name AS export_type
59+
FROM nt_nameserver ns
60+
JOIN nt_nameserver_export_type t ON ns.export_type_id=t.id`,
61+
mapToDbColumn(args, nsDbMap),
62+
),
63+
)
64+
for (const r of rows) {
65+
for (const b of boolFields) {
66+
r[b] = r[b] === 1
67+
}
68+
}
69+
return dbToObject(rows)
70+
}
71+
72+
async put(args) {
73+
if (!args.id) return false
74+
const id = args.id
75+
delete args.id
76+
// Mysql.debug(1)
77+
const r = await Mysql.execute(
78+
...Mysql.update(
79+
`nt_nameserver`,
80+
`nt_nameserver_id=${id}`,
81+
mapToDbColumn(args, nsDbMap),
82+
),
83+
)
84+
// console.log(r)
85+
return r.changedRows === 1
86+
}
87+
88+
async delete(args) {
89+
await Mysql.execute(
90+
`UPDATE nt_nameserver SET deleted=? WHERE nt_nameserver_id=?`,
91+
[args.deleted ?? 1, args.id],
92+
)
93+
return true
94+
}
95+
96+
async destroy(args) {
97+
return await Mysql.execute(
98+
...Mysql.delete(`nt_nameserver`, { nt_nameserver_id: args.id }),
99+
)
100+
}
101+
}
102+
103+
export default new Nameserver()
104+
105+
function dbToObject(rows) {
106+
for (const row of rows) {
107+
for (const f of [
108+
'description',
109+
'address6',
110+
'remote_login',
111+
'datadir',
112+
'logdir',
113+
'export_status',
114+
]) {
115+
if ([undefined, null].includes(row[f])) row[f] = ''
116+
}
117+
for (const f of ['export']) {
118+
for (const p of ['type', 'interval', 'serials', 'status']) {
119+
if (row[`${f}_${p}`] !== undefined) {
120+
if (row[f] === undefined) row[f] = {}
121+
row[f][p] = row[`${f}_${p}`]
122+
delete row[`${f}_${p}`]
123+
}
124+
}
125+
}
126+
}
127+
return rows
128+
}
129+
130+
function objectToDb(row) {
131+
row = JSON.parse(JSON.stringify(row)) // don't mutate the original
132+
133+
for (const f of ['export']) {
134+
for (const p of ['interval', 'serials', 'status']) {
135+
if (row[f] === undefined) continue
136+
if (row[f][p] === undefined) continue
137+
row[`${f}_${p}`] = row[f][p]
138+
delete row[f][p]
139+
}
140+
delete row[f]
141+
}
142+
return row
143+
}

lib/nameserver.test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import assert from 'node:assert/strict'
2+
import { describe, it, after, before } from 'node:test'
3+
4+
import Nameserver from './nameserver.js'
5+
6+
import testCase from './test/nameserver.json' with { type: 'json' }
7+
8+
before(async () => {
9+
await Nameserver.destroy({ id: testCase.id })
10+
await Nameserver.create(testCase)
11+
})
12+
13+
after(async () => {
14+
await Nameserver.destroy({ id: testCase.id })
15+
Nameserver.mysql.disconnect()
16+
})
17+
18+
describe('nameserver', function () {
19+
it('gets nameserver by id', async () => {
20+
const g = await Nameserver.get({ id: testCase.id })
21+
assert.deepEqual(g[0], testCase)
22+
})
23+
24+
it('gets nameserver by name', async () => {
25+
const g = await Nameserver.get({ name: testCase.name })
26+
assert.deepEqual(g[0], testCase)
27+
})
28+
29+
it('changes a nameserver', async () => {
30+
assert.ok(
31+
await Nameserver.put({ id: testCase.id, name: 'b.ns.example.com.' }),
32+
)
33+
const ns = await Nameserver.get({ id: testCase.id })
34+
assert.deepEqual(ns[0].name, 'b.ns.example.com.')
35+
assert.ok(await Nameserver.put({ id: testCase.id, name: testCase.name }))
36+
})
37+
38+
it('deletes a nameserver', async () => {
39+
assert.ok(await Nameserver.delete({ id: testCase.id }))
40+
let g = await Nameserver.get({ id: testCase.id, deleted: 1 })
41+
assert.equal(g[0]?.deleted, true)
42+
await Nameserver.delete({ id: testCase.id, deleted: 0 }) // restore
43+
g = await Nameserver.get({ id: testCase.id })
44+
assert.equal(g[0].deleted, false)
45+
})
46+
})

lib/test/nameserver.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"id": 4096,
3+
"gid": 4096,
4+
"name": "a.ns.example.com.",
5+
"description": "",
6+
"address": "1.2.3.4",
7+
"address6": "2001:DB8::1",
8+
"remote_login": "nsd",
9+
"logdir": "/foo",
10+
"datadir": "/bar",
11+
"export": {
12+
"interval": 0,
13+
"serials": true,
14+
"status": "last run:03-05 15:25<br>last cp :09-20 12:59",
15+
"type": "NSD"
16+
},
17+
"ttl": 3600,
18+
"deleted": false
19+
}

lib/util.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,15 @@ const meta = {
2424
}
2525

2626
function mapToDbColumn(args, maps) {
27-
// create an instance, so we don't mangle the original args
28-
const newArgs = JSON.parse(JSON.stringify(args))
27+
args = JSON.parse(JSON.stringify(args)) // don't mutate the original
2928

3029
for (const [key, val] of Object.entries(maps)) {
31-
if (newArgs[key] !== undefined) {
32-
newArgs[val] = newArgs[key]
33-
delete newArgs[key]
30+
if (args[key] !== undefined) {
31+
args[val] = args[key]
32+
delete args[key]
3433
}
3534
}
36-
return newArgs
35+
return args
3736
}
3837

3938
export { setEnv, meta, mapToDbColumn }

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nictool/api",
3-
"version": "3.0.0-alpha.3",
3+
"version": "3.0.0-alpha.4",
44
"description": "NicTool API",
55
"main": "index.js",
66
"type": "module",
@@ -43,7 +43,7 @@
4343
"@hapi/hoek": "^11.0.4",
4444
"@hapi/inert": "^7.1.0",
4545
"@hapi/vision": "^7.0.3",
46-
"@nictool/validate": "^0.7.4",
46+
"@nictool/validate": "^0.8.0",
4747
"hapi-swagger": "^17.2.1",
4848
"mysql2": "^3.9.2",
4949
"qs": "^6.11.2",

routes/group.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ function GroupRoutes(server) {
99
method: 'GET',
1010
path: '/group/{id}',
1111
options: {
12+
validate: {
13+
query: validate.group.GET_req,
14+
},
1215
response: {
13-
schema: validate.group.GET,
16+
schema: validate.group.GET_res,
1417
},
1518
tags: ['api'],
1619
},
@@ -50,15 +53,12 @@ function GroupRoutes(server) {
5053
payload: validate.group.POST,
5154
},
5255
response: {
53-
schema: validate.group.GET,
56+
schema: validate.group.GET_res,
5457
},
5558
tags: ['api'],
5659
},
5760
handler: async (request, h) => {
5861
const gid = await Group.create(request.payload)
59-
if (!gid) {
60-
console.log(`POST /group oops`) // TODO
61-
}
6262

6363
const groups = await Group.get({ id: gid })
6464

@@ -77,13 +77,16 @@ function GroupRoutes(server) {
7777
method: 'DELETE',
7878
path: '/group/{id}',
7979
options: {
80+
validate: {
81+
query: validate.group.DELETE,
82+
},
8083
response: {
81-
schema: validate.group.GET,
84+
schema: validate.group.GET_res,
8285
},
8386
tags: ['api'],
8487
},
8588
handler: async (request, h) => {
86-
const groups = await Group.get(request.params)
89+
const groups = await Group.get({ id: parseInt(request.params.id, 10) })
8790
/* c8 ignore next 10 */
8891
if (groups.length !== 1) {
8992
return h
@@ -96,8 +99,7 @@ function GroupRoutes(server) {
9699
.code(204)
97100
}
98101

99-
const action = request.query.destroy === 'true' ? 'destroy' : 'delete'
100-
await Group[action]({ id: groups[0].id })
102+
await Group.delete({ id: groups[0].id })
101103
delete groups[0].gid
102104

103105
return h

routes/group.test.js

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import groupCase from './test/group.json' with { type: 'json' }
99
import userCase from './test/user.json' with { type: 'json' }
1010

1111
let server
12+
const case2Id = 4094
1213

1314
before(async () => {
1415
server = await init()
@@ -17,7 +18,8 @@ before(async () => {
1718
})
1819

1920
after(async () => {
20-
await server.stop()
21+
await Group.destroy({ id: case2Id })
22+
server.stop()
2123
})
2224

2325
describe('group routes', () => {
@@ -45,11 +47,9 @@ describe('group routes', () => {
4547
},
4648
})
4749
// console.log(res.result)
48-
assert.equal(res.statusCode, 200)
50+
assert.ok([200, 204].includes(res.statusCode))
4951
})
5052

51-
const case2Id = 4094
52-
5353
it('POST /group', async () => {
5454
const testCase = JSON.parse(JSON.stringify(groupCase))
5555
testCase.id = case2Id // make it unique
@@ -78,7 +78,7 @@ describe('group routes', () => {
7878
},
7979
})
8080
// console.log(res.result)
81-
assert.equal(res.statusCode, 200)
81+
assert.ok([200, 204].includes(res.statusCode))
8282
})
8383

8484
it(`DELETE /group/${case2Id}`, async () => {
@@ -108,25 +108,13 @@ describe('group routes', () => {
108108
it(`GET /group/${case2Id} (deleted)`, async () => {
109109
const res = await server.inject({
110110
method: 'GET',
111-
url: `/group/${case2Id}?deleted=1`,
111+
url: `/group/${case2Id}?deleted=true`,
112112
headers: {
113113
Cookie: sessionCookie,
114114
},
115115
})
116116
// console.log(res.result)
117-
assert.equal(res.statusCode, 200)
118-
})
119-
120-
it(`DELETE /group/${case2Id}`, async () => {
121-
const res = await server.inject({
122-
method: 'DELETE',
123-
url: `/group/${case2Id}?destroy=true`,
124-
headers: {
125-
Cookie: sessionCookie,
126-
},
127-
})
128-
// console.log(res.result)
129-
assert.equal(res.statusCode, 200)
117+
assert.ok([200, 204].includes(res.statusCode))
130118
})
131119

132120
it('DELETE /session', async () => {

0 commit comments

Comments
 (0)