Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4910 from tmakar/webd-bulk-kdb-set
Browse files Browse the repository at this point in the history
`Webd` and `elektrad` bulk kdb meta-set
  • Loading branch information
markus2330 authored May 1, 2023
2 parents c8e5d5a + 984f986 commit ef00c96
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 26 deletions.
10 changes: 6 additions & 4 deletions doc/news/_preparation_next_release.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,15 +291,15 @@ This section keeps you up-to-date with the multi-language support provided by El

## Tools

### <<Tool>>
### elektrad

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

### <<Tool>>
### webd

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

Expand Down Expand Up @@ -355,6 +355,8 @@ This section keeps you up-to-date with the multi-language support provided by El
- Added Hannes Laimer to `AUTHORS.md` _(Hannes Laimer @hannes99)_
- Add README to tools/kdb _(Hannes Laimer @hannes99)_
- <<TODO>>
- Fixed shell-recorder test in [`install-webui.md`](../tutorials/install-webui.md) _(Tomislav Makar @tmakar)_
- 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)_
- <<TODO>>
- <<TODO>>
- <<TODO>>
Expand Down
44 changes: 41 additions & 3 deletions doc/tutorials/install-webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ First create a new key-value pair `user:/test` and set its value to 5. This can
```
- through the rest API using curl
```sh
curl -X PUT -H "Content-Type: text/plain" --data "5" http://localhost:33333/kdb/user/test
curl -X PUT -H "Content-Type: text/plain" --data "5" http://localhost:33333/kdb/user:/test
```

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

```sh
curl http://localhost:33333/kdb/user/test
#> {"exists":true,"name":"test","path":"user/test","ls":["user/test"],"value":"5","meta":""}
curl http://localhost:33333/kdb/user:/test
#> {"exists":true,"name":"test","path":"user:/test","ls":["user:/test"],"value":"5","meta":""}
```

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

<!-- prettier-ignore-end -->

The command

```sh
curl -X POST -H "Content-Type: application/json" -d '{"meta": [{"key": "metakey1", "value": "value1"},{"key": "metakey2", "value": "value2"}]}' http://localhost:33333/kdbMetaBulk/user:/test
```

will now create multiple metakeys at once.
In this case, it will create two (`metakey1` and `metakey2`).

The command

```sh
curl http://localhost:33333/kdb/user:/test
#> {"exists":true,"name":"test","path":"user:/test","ls":["user:/test"],"value":"1","meta":{"metakey1":"value1","metakey2":"value2"}}
```

will now also return the two metakeys.

<!-- prettier-ignore-start -->

```json
{
"exists": true,
"name": "test",
"path": "user:/test",
"ls": [
"user:/test"
],
"value": "5",
"meta": {
"metakey1": "value1",
"metakey2": "value2"
}
}
```

<!-- prettier-ignore-end -->

## Auth

Currently, webd does not support authentication. The best way to work around
Expand Down
19 changes: 19 additions & 0 deletions src/tools/elektrad/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,22 @@ func getKey(t *testing.T, keyName string) elektra.Key {

return ks.Lookup(parentKey)
}

func containsMeta(t *testing.T, keyName string, expectedMeta []keyValueBody) {
key := getKey(t, keyName)
removeKey(t, keyName)

for _, actualMeta := range key.MetaSlice() {
metaName := actualMeta.Name()
metaValue := actualMeta.String()

found := false
for _, expectedMeta := range expectedMeta {
if expectedMeta.Key == metaName && *expectedMeta.Value == metaValue {
found = true
}
}

Assertf(t, found, "Expected meta name %s with value %s not found", metaName, metaValue)
}
}
135 changes: 119 additions & 16 deletions src/tools/elektrad/meta_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,23 @@ type keyValueBody struct {
Value *string `json:"value"`
}

type metaKeySet struct {
Meta []keyValueBody `json:"meta"`
}

// postMetaHandler sets a Meta value on a key if a value was passed,
// and deletes the existing Meta value if not.
//
// Arguments:
// keyName the name of the key. URL path param.
// key the name of the metaKey. Passed through the key field of the JSON body.
// value the value of the metaKey. Passed through the `value` field of the JSON body.
//
// keyName the name of the key. URL path param.
// key the name of the metaKey. Passed through the key field of the JSON body.
// value the value of the metaKey. Passed through the `value` field of the JSON body.
//
// Response Code:
// 201 No Content if the request is successfull.
// 401 Bad Request if no key name was passed - or the key name is invalid.
//
// 201 No Content if the request is successful.
// 401 Bad Request if no key name was passed - or the key name is invalid.
//
// Example: `curl -X POST -d '{ "key": "hello", "value": "world" }' localhost:33333/kdbMeta/user/test/hello`
func (s *server) postMetaHandler(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -75,37 +81,134 @@ func (s *server) postMetaHandler(w http.ResponseWriter, r *http.Request) {
ks.AppendKey(parentKey)
}

if meta.Value == nil {
err = k.RemoveMeta(meta.Key)
} else {
err = k.SetMeta(meta.Key, *meta.Value)
metaKeys := []keyValueBody{meta}
if err = removeOrSetMetaKeys(k, errKey, handle, ks, metaKeySet{metaKeys}); err != nil {
writeError(w, err)
return
}

if err != nil {
noContent(w)
}

// postMetaBulkHandler sets a whole set of metadata. In case there is a metakey with empty value it deletes the existing
// Meta value.
//
// Arguments:
//
// keyName the name of the key. URL path param.
// metaSet the set of metakeys for the given keyName
//
// Response Code:
//
// 201 No Content if the request is successful.
// 401 Bad Request if no key name was passed - or the key name is invalid.
//
// Example: `curl -X POST -d '{ meta: [{"key": "hello", "value": "world"}] }' localhost:33333/kdbMetaBulk/user/test/hello`
func (s *server) postMetaBulkHandler(w http.ResponseWriter, r *http.Request) {
var metaKeySet metaKeySet

keyName := parseKeyNameFromURL(r)

decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&metaKeySet); err != nil {
writeError(w, err)
return
}

err = set(handle, ks, errKey)
if metaKeySet.Meta == nil {
badRequest(w)
return
}

errKey, err := elektra.NewKey(keyName)

if err != nil {
internalServerError(w)
return
}

defer errKey.Close()

parentKey, err := elektra.NewKey(keyName)

if err != nil {
badRequest(w)
return
}

defer parentKey.Close()

handle, ks := getHandle(r)

if _, err = handle.Get(ks, errKey); err != nil {
writeError(w, err)
return
}

k := ks.LookupByName(keyName)

if k == nil {
k = parentKey
ks.AppendKey(parentKey)
}

if err = removeOrSetMetaKeys(k, errKey, handle, ks, metaKeySet); err != nil {
writeError(w, err)
return
}

noContent(w)
}

// removeOrSetMetaKeys removes or sets a set of metakeys for a given k
//
// Arguments:
//
// k the key to append metakeys too
// errKey the key to append errors too
// handle the KDB handle
// ks the KeySet the key is located in
// metaKeys the set of metakeys to append to k
//
// Return:
//
// error in case it case the set metakey operation failed
func removeOrSetMetaKeys(k elektra.Key, errKey elektra.Key, handle elektra.KDB, ks elektra.KeySet, metaKeys metaKeySet) error {
var err error

for _, meta := range metaKeys.Meta {
if meta.Value == nil {
err = k.RemoveMeta(meta.Key)
} else {
err = k.SetMeta(meta.Key, *meta.Value)
}

if err != nil {
return err
}

err = set(handle, ks, errKey)

if err != nil {
return err
}
}

return err
}

// deleteMetaHandler deletes a Meta key.
//
// Arguments:
// keyName the name of the Key.
// key the name of the metaKey. Passed through the key field of the JSON body.
//
// keyName the name of the Key.
// key the name of the metaKey. Passed through the key field of the JSON body.
//
// Response Code:
// 201 No Content if the request is successfull.
// 401 Bad Request if no key name was passed - or the key name is invalid.
// 404 Not Found if the key was not found.
//
// 201 No Content if the request is successful.
// 401 Bad Request if no key name was passed - or the key name is invalid.
// 404 Not Found if the key was not found.
//
// Example: `curl -X DELETE -d '{ "key": "hello" }' localhost:33333/kdbMeta/user/test/hello`
func (s *server) deleteMetaHandler(w http.ResponseWriter, r *http.Request) {
Expand Down
24 changes: 24 additions & 0 deletions src/tools/elektrad/meta_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ func TestPostMeta(t *testing.T) {
Assert(t, key.Meta("postmeta") == value, "key has wrong meta value")
}

func TestPostMetaBulk(t *testing.T) {
keyName := "user:/tests/elektrad/kdbmetabulk/post"
value := "Bulk set meta keys test value"

metaOne := keyValueBody{
Key: "postmetabulkone",
Value: &value,
}
metaTwo := keyValueBody{
Key: "postmetabulktwo",
Value: &value,
}

setupKey(t, keyName)

metaSet := []keyValueBody{metaOne, metaTwo}

w := testPost(t, "/kdbMetaBulk/"+keyName, metaKeySet{metaSet})
code := w.Result().StatusCode
Assertf(t, code == http.StatusNoContent, "wrong status code: %v", code)

containsMeta(t, keyName, metaSet)
}

func TestDeleteMetaHandler(t *testing.T) {
keyName := "user:/tests/elektrad/kdbmeta/delete/test"
value := "value"
Expand Down
1 change: 1 addition & 0 deletions src/tools/elektrad/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func setupRouter(app *server) http.Handler {
// r.HandleFunc("/kdbCp/{path:.*", app.postCopyHandler).Methods("POST")

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

return r
Expand Down
4 changes: 2 additions & 2 deletions src/tools/webd/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion src/tools/webd/src/connector.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ const setmeta = (host, path, key, value) =>
return { status: res.status };
});

const setmetabulk = (host, path, keySet) =>
fetch(`${host}/kdbMetaBulk/${encodePath(path)}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(keySet),
}).then((res) => {
return { status: res.status };
});

const rmmeta = (host, path, key) =>
fetch(`${host}/kdbMeta/${encodePath(path)}`, {
method: "DELETE",
Expand All @@ -97,4 +108,15 @@ const rmmeta = (host, path, key) =>
return { status: res.status };
});

export default { version, get, set, rm, mv, cp, setmeta, rmmeta, find };
export default {
version,
get,
set,
rm,
mv,
cp,
setmeta,
setmetabulk,
rmmeta,
find,
};
12 changes: 12 additions & 0 deletions src/tools/webd/src/routes/instances.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,16 @@ export default function initInstanceRoutes(app) {
.then(() => res.status(204).send())
.catch((err) => errorResponse(res, err))
);

app.route("/api/instances/:id/kdbMetaBulk/*").post((req, res) =>
getInstance(req.params.id)
.then((instance) =>
remoteKdb.setmetabulk(instance.host, req.params[0], req.body)
)
.then((instanceRes) =>
setSessionID(req.params.id, req.session, instanceRes)
)
.then(() => res.status(204).send())
.catch((err) => errorResponse(res, err))
);
}

0 comments on commit ef00c96

Please sign in to comment.