Skip to content
This repository was archived by the owner on Feb 16, 2025. It is now read-only.

Commit ef00c96

Browse files
authored
Merge pull request #4910 from tmakar/webd-bulk-kdb-set
`Webd` and `elektrad` bulk kdb meta-set
2 parents c8e5d5a + 984f986 commit ef00c96

9 files changed

+247
-26
lines changed

doc/news/_preparation_next_release.md

+6-4
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,15 @@ This section keeps you up-to-date with the multi-language support provided by El
291291

292292
## Tools
293293

294-
### <<Tool>>
294+
### elektrad
295295

296-
- <<TODO>>
296+
- Implemented new request to add multiple metakeys for one key _(Tomislav Makar @tmakar)_
297297
- <<TODO>>
298298
- <<TODO>>
299299

300-
### <<Tool>>
300+
### webd
301301

302-
- <<TODO>>
302+
- Implemented new request to add multiple metakeys for one key _(Tomislav Makar @tmakar)_
303303
- <<TODO>>
304304
- <<TODO>>
305305

@@ -355,6 +355,8 @@ This section keeps you up-to-date with the multi-language support provided by El
355355
- Added Hannes Laimer to `AUTHORS.md` _(Hannes Laimer @hannes99)_
356356
- Add README to tools/kdb _(Hannes Laimer @hannes99)_
357357
- <<TODO>>
358+
- Fixed shell-recorder test in [`install-webui.md`](../tutorials/install-webui.md) _(Tomislav Makar @tmakar)_
359+
- Add new shell-recorder test in [`install-webui.md`](../tutorials/install-webui.md) for newly implemented request for adding multiple metakeys for one key _(Tomislav Makar @tmakar)_
358360
- <<TODO>>
359361
- <<TODO>>
360362
- <<TODO>>

doc/tutorials/install-webui.md

+41-3
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ First create a new key-value pair `user:/test` and set its value to 5. This can
132132
```
133133
- through the rest API using curl
134134
```sh
135-
curl -X PUT -H "Content-Type: text/plain" --data "5" http://localhost:33333/kdb/user/test
135+
curl -X PUT -H "Content-Type: text/plain" --data "5" http://localhost:33333/kdb/user:/test
136136
```
137137

138138
The output of the commandline tool will be `Set string to "5"` if the key did not exist before.
@@ -142,8 +142,8 @@ Elektrad will respond with code `200`.
142142
The command
143143

144144
```sh
145-
curl http://localhost:33333/kdb/user/test
146-
#> {"exists":true,"name":"test","path":"user/test","ls":["user/test"],"value":"5","meta":""}
145+
curl http://localhost:33333/kdb/user:/test
146+
#> {"exists":true,"name":"test","path":"user:/test","ls":["user:/test"],"value":"5","meta":""}
147147
```
148148

149149
will now return the value of the specified key `user:/test`, which is stored in the database.
@@ -165,6 +165,44 @@ will now return the value of the specified key `user:/test`, which is stored in
165165

166166
<!-- prettier-ignore-end -->
167167

168+
The command
169+
170+
```sh
171+
curl -X POST -H "Content-Type: application/json" -d '{"meta": [{"key": "metakey1", "value": "value1"},{"key": "metakey2", "value": "value2"}]}' http://localhost:33333/kdbMetaBulk/user:/test
172+
```
173+
174+
will now create multiple metakeys at once.
175+
In this case, it will create two (`metakey1` and `metakey2`).
176+
177+
The command
178+
179+
```sh
180+
curl http://localhost:33333/kdb/user:/test
181+
#> {"exists":true,"name":"test","path":"user:/test","ls":["user:/test"],"value":"1","meta":{"metakey1":"value1","metakey2":"value2"}}
182+
```
183+
184+
will now also return the two metakeys.
185+
186+
<!-- prettier-ignore-start -->
187+
188+
```json
189+
{
190+
"exists": true,
191+
"name": "test",
192+
"path": "user:/test",
193+
"ls": [
194+
"user:/test"
195+
],
196+
"value": "5",
197+
"meta": {
198+
"metakey1": "value1",
199+
"metakey2": "value2"
200+
}
201+
}
202+
```
203+
204+
<!-- prettier-ignore-end -->
205+
168206
## Auth
169207

170208
Currently, webd does not support authentication. The best way to work around

src/tools/elektrad/helper_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,22 @@ func getKey(t *testing.T, keyName string) elektra.Key {
183183

184184
return ks.Lookup(parentKey)
185185
}
186+
187+
func containsMeta(t *testing.T, keyName string, expectedMeta []keyValueBody) {
188+
key := getKey(t, keyName)
189+
removeKey(t, keyName)
190+
191+
for _, actualMeta := range key.MetaSlice() {
192+
metaName := actualMeta.Name()
193+
metaValue := actualMeta.String()
194+
195+
found := false
196+
for _, expectedMeta := range expectedMeta {
197+
if expectedMeta.Key == metaName && *expectedMeta.Value == metaValue {
198+
found = true
199+
}
200+
}
201+
202+
Assertf(t, found, "Expected meta name %s with value %s not found", metaName, metaValue)
203+
}
204+
}

src/tools/elektrad/meta_handler.go

+119-16
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,23 @@ type keyValueBody struct {
1212
Value *string `json:"value"`
1313
}
1414

15+
type metaKeySet struct {
16+
Meta []keyValueBody `json:"meta"`
17+
}
18+
1519
// postMetaHandler sets a Meta value on a key if a value was passed,
1620
// and deletes the existing Meta value if not.
1721
//
1822
// Arguments:
19-
// keyName the name of the key. URL path param.
20-
// key the name of the metaKey. Passed through the key field of the JSON body.
21-
// value the value of the metaKey. Passed through the `value` field of the JSON body.
23+
//
24+
// keyName the name of the key. URL path param.
25+
// key the name of the metaKey. Passed through the key field of the JSON body.
26+
// value the value of the metaKey. Passed through the `value` field of the JSON body.
2227
//
2328
// Response Code:
24-
// 201 No Content if the request is successfull.
25-
// 401 Bad Request if no key name was passed - or the key name is invalid.
29+
//
30+
// 201 No Content if the request is successful.
31+
// 401 Bad Request if no key name was passed - or the key name is invalid.
2632
//
2733
// Example: `curl -X POST -d '{ "key": "hello", "value": "world" }' localhost:33333/kdbMeta/user/test/hello`
2834
func (s *server) postMetaHandler(w http.ResponseWriter, r *http.Request) {
@@ -75,37 +81,134 @@ func (s *server) postMetaHandler(w http.ResponseWriter, r *http.Request) {
7581
ks.AppendKey(parentKey)
7682
}
7783

78-
if meta.Value == nil {
79-
err = k.RemoveMeta(meta.Key)
80-
} else {
81-
err = k.SetMeta(meta.Key, *meta.Value)
84+
metaKeys := []keyValueBody{meta}
85+
if err = removeOrSetMetaKeys(k, errKey, handle, ks, metaKeySet{metaKeys}); err != nil {
86+
writeError(w, err)
87+
return
8288
}
8389

84-
if err != nil {
90+
noContent(w)
91+
}
92+
93+
// postMetaBulkHandler sets a whole set of metadata. In case there is a metakey with empty value it deletes the existing
94+
// Meta value.
95+
//
96+
// Arguments:
97+
//
98+
// keyName the name of the key. URL path param.
99+
// metaSet the set of metakeys for the given keyName
100+
//
101+
// Response Code:
102+
//
103+
// 201 No Content if the request is successful.
104+
// 401 Bad Request if no key name was passed - or the key name is invalid.
105+
//
106+
// Example: `curl -X POST -d '{ meta: [{"key": "hello", "value": "world"}] }' localhost:33333/kdbMetaBulk/user/test/hello`
107+
func (s *server) postMetaBulkHandler(w http.ResponseWriter, r *http.Request) {
108+
var metaKeySet metaKeySet
109+
110+
keyName := parseKeyNameFromURL(r)
111+
112+
decoder := json.NewDecoder(r.Body)
113+
if err := decoder.Decode(&metaKeySet); err != nil {
85114
writeError(w, err)
86115
return
87116
}
88117

89-
err = set(handle, ks, errKey)
118+
if metaKeySet.Meta == nil {
119+
badRequest(w)
120+
return
121+
}
122+
123+
errKey, err := elektra.NewKey(keyName)
124+
125+
if err != nil {
126+
internalServerError(w)
127+
return
128+
}
129+
130+
defer errKey.Close()
131+
132+
parentKey, err := elektra.NewKey(keyName)
90133

91134
if err != nil {
135+
badRequest(w)
136+
return
137+
}
138+
139+
defer parentKey.Close()
140+
141+
handle, ks := getHandle(r)
142+
143+
if _, err = handle.Get(ks, errKey); err != nil {
144+
writeError(w, err)
145+
return
146+
}
147+
148+
k := ks.LookupByName(keyName)
149+
150+
if k == nil {
151+
k = parentKey
152+
ks.AppendKey(parentKey)
153+
}
154+
155+
if err = removeOrSetMetaKeys(k, errKey, handle, ks, metaKeySet); err != nil {
92156
writeError(w, err)
93157
return
94158
}
95159

96160
noContent(w)
97161
}
98162

163+
// removeOrSetMetaKeys removes or sets a set of metakeys for a given k
164+
//
165+
// Arguments:
166+
//
167+
// k the key to append metakeys too
168+
// errKey the key to append errors too
169+
// handle the KDB handle
170+
// ks the KeySet the key is located in
171+
// metaKeys the set of metakeys to append to k
172+
//
173+
// Return:
174+
//
175+
// error in case it case the set metakey operation failed
176+
func removeOrSetMetaKeys(k elektra.Key, errKey elektra.Key, handle elektra.KDB, ks elektra.KeySet, metaKeys metaKeySet) error {
177+
var err error
178+
179+
for _, meta := range metaKeys.Meta {
180+
if meta.Value == nil {
181+
err = k.RemoveMeta(meta.Key)
182+
} else {
183+
err = k.SetMeta(meta.Key, *meta.Value)
184+
}
185+
186+
if err != nil {
187+
return err
188+
}
189+
190+
err = set(handle, ks, errKey)
191+
192+
if err != nil {
193+
return err
194+
}
195+
}
196+
197+
return err
198+
}
199+
99200
// deleteMetaHandler deletes a Meta key.
100201
//
101202
// Arguments:
102-
// keyName the name of the Key.
103-
// key the name of the metaKey. Passed through the key field of the JSON body.
203+
//
204+
// keyName the name of the Key.
205+
// key the name of the metaKey. Passed through the key field of the JSON body.
104206
//
105207
// Response Code:
106-
// 201 No Content if the request is successfull.
107-
// 401 Bad Request if no key name was passed - or the key name is invalid.
108-
// 404 Not Found if the key was not found.
208+
//
209+
// 201 No Content if the request is successful.
210+
// 401 Bad Request if no key name was passed - or the key name is invalid.
211+
// 404 Not Found if the key was not found.
109212
//
110213
// Example: `curl -X DELETE -d '{ "key": "hello" }' localhost:33333/kdbMeta/user/test/hello`
111214
func (s *server) deleteMetaHandler(w http.ResponseWriter, r *http.Request) {

src/tools/elektrad/meta_handler_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,30 @@ func TestPostMeta(t *testing.T) {
2828
Assert(t, key.Meta("postmeta") == value, "key has wrong meta value")
2929
}
3030

31+
func TestPostMetaBulk(t *testing.T) {
32+
keyName := "user:/tests/elektrad/kdbmetabulk/post"
33+
value := "Bulk set meta keys test value"
34+
35+
metaOne := keyValueBody{
36+
Key: "postmetabulkone",
37+
Value: &value,
38+
}
39+
metaTwo := keyValueBody{
40+
Key: "postmetabulktwo",
41+
Value: &value,
42+
}
43+
44+
setupKey(t, keyName)
45+
46+
metaSet := []keyValueBody{metaOne, metaTwo}
47+
48+
w := testPost(t, "/kdbMetaBulk/"+keyName, metaKeySet{metaSet})
49+
code := w.Result().StatusCode
50+
Assertf(t, code == http.StatusNoContent, "wrong status code: %v", code)
51+
52+
containsMeta(t, keyName, metaSet)
53+
}
54+
3155
func TestDeleteMetaHandler(t *testing.T) {
3256
keyName := "user:/tests/elektrad/kdbmeta/delete/test"
3357
value := "value"

src/tools/elektrad/router.go

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func setupRouter(app *server) http.Handler {
3030
// r.HandleFunc("/kdbCp/{path:.*", app.postCopyHandler).Methods("POST")
3131

3232
r.HandleFunc("/kdbMeta/{path:.*}", app.postMetaHandler).Methods("POST")
33+
r.HandleFunc("/kdbMetaBulk/{path:.*}", app.postMetaBulkHandler).Methods("POST")
3334
r.HandleFunc("/kdbMeta/{path:.*}", app.deleteMetaHandler).Methods("DELETE")
3435

3536
return r

src/tools/webd/package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/webd/src/connector.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ const setmeta = (host, path, key, value) =>
8686
return { status: res.status };
8787
});
8888

89+
const setmetabulk = (host, path, keySet) =>
90+
fetch(`${host}/kdbMetaBulk/${encodePath(path)}`, {
91+
method: "POST",
92+
headers: {
93+
"Content-Type": "application/json",
94+
},
95+
body: JSON.stringify(keySet),
96+
}).then((res) => {
97+
return { status: res.status };
98+
});
99+
89100
const rmmeta = (host, path, key) =>
90101
fetch(`${host}/kdbMeta/${encodePath(path)}`, {
91102
method: "DELETE",
@@ -97,4 +108,15 @@ const rmmeta = (host, path, key) =>
97108
return { status: res.status };
98109
});
99110

100-
export default { version, get, set, rm, mv, cp, setmeta, rmmeta, find };
111+
export default {
112+
version,
113+
get,
114+
set,
115+
rm,
116+
mv,
117+
cp,
118+
setmeta,
119+
setmetabulk,
120+
rmmeta,
121+
find,
122+
};

src/tools/webd/src/routes/instances.js

+12
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,16 @@ export default function initInstanceRoutes(app) {
225225
.then(() => res.status(204).send())
226226
.catch((err) => errorResponse(res, err))
227227
);
228+
229+
app.route("/api/instances/:id/kdbMetaBulk/*").post((req, res) =>
230+
getInstance(req.params.id)
231+
.then((instance) =>
232+
remoteKdb.setmetabulk(instance.host, req.params[0], req.body)
233+
)
234+
.then((instanceRes) =>
235+
setSessionID(req.params.id, req.session, instanceRes)
236+
)
237+
.then(() => res.status(204).send())
238+
.catch((err) => errorResponse(res, err))
239+
);
228240
}

0 commit comments

Comments
 (0)