mirror of
https://github.com/jech/galene.git
synced 2024-11-22 08:35:57 +01:00
Use JSON arrays instead of plain text in API.
Suggested by Dianne Skoll.
This commit is contained in:
parent
aa35408dba
commit
a12331ee09
4 changed files with 42 additions and 61 deletions
14
README.API
14
README.API
|
@ -43,8 +43,8 @@ allowed methods are HEAD and GET.
|
||||||
|
|
||||||
/galene-api/v0/.groups/
|
/galene-api/v0/.groups/
|
||||||
|
|
||||||
Returns a list of groups, as plain text, one per line. The only allowed
|
Returns a list of groups, as a JSON array. The only allowed methods are
|
||||||
methods are HEAD and GET.
|
HEAD and GET.
|
||||||
|
|
||||||
### Group definition
|
### Group definition
|
||||||
|
|
||||||
|
@ -75,8 +75,8 @@ accepted content-type is `application/jwk-set+json`.
|
||||||
|
|
||||||
/galene-api/v0/.groups/groupname/.users/
|
/galene-api/v0/.groups/groupname/.users/
|
||||||
|
|
||||||
Returns a list of users, as plain text, one per line. The only allowed
|
Returns a list of users, as a JSON array. The only allowed methods are
|
||||||
methods are HEAD and GET.
|
HEAD and GET.
|
||||||
|
|
||||||
### User definition
|
### User definition
|
||||||
|
|
||||||
|
@ -101,9 +101,9 @@ POST.
|
||||||
|
|
||||||
/galene-api/v0/.groups/groupname/.users/username/.tokens/
|
/galene-api/v0/.groups/groupname/.users/username/.tokens/
|
||||||
|
|
||||||
GET returns the list of stateful tokens, as plain text, one token per
|
GET returns the list of stateful tokens, as a JSON array. POST creates
|
||||||
line. POST creates a new token, and returns its name in the `Location`
|
a new token, and returns its name in the `Location` header. Allowed
|
||||||
header. Allowed methods are HEAD, GET and POST.
|
methods are HEAD, GET and POST.
|
||||||
|
|
||||||
### Stateful token
|
### Stateful token
|
||||||
|
|
||||||
|
|
|
@ -49,12 +49,11 @@ async function listObjects(url) {
|
||||||
let r = await fetch(url);
|
let r = await fetch(url);
|
||||||
if(!r.ok)
|
if(!r.ok)
|
||||||
throw httpError(r);
|
throw httpError(r);
|
||||||
let strings = (await r.text()).split('\n');
|
let data = await r.json();
|
||||||
if(strings[strings.length - 1] === '') {
|
if(!(data instanceof Array))
|
||||||
strings.pop();
|
throw new Error("Server didn't return array");
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
return strings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* createObject makes a PUT request to url with JSON data.
|
* createObject makes a PUT request to url with JSON data.
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -118,13 +117,12 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("content-type", "text/plain; charset=utf-8")
|
w.Header().Set("content-type", "application/json")
|
||||||
if r.Method == "HEAD" {
|
if r.Method == "HEAD" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, g := range groups {
|
e := json.NewEncoder(w)
|
||||||
fmt.Fprintln(w, g)
|
e.Encode(groups)
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +255,7 @@ func usersHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("content-type", "text/plain; charset=utf-8")
|
w.Header().Set("content-type", "application/json")
|
||||||
w.Header().Set("etag", etag)
|
w.Header().Set("etag", etag)
|
||||||
done := checkPreconditions(w, r, etag)
|
done := checkPreconditions(w, r, etag)
|
||||||
if done {
|
if done {
|
||||||
|
@ -266,9 +264,8 @@ func usersHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
|
||||||
if r.Method == "HEAD" {
|
if r.Method == "HEAD" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, u := range users {
|
e := json.NewEncoder(w)
|
||||||
fmt.Fprintln(w, u)
|
e.Encode(users)
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,17 +542,19 @@ func tokensHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("content-type",
|
w.Header().Set("content-type", "application/json")
|
||||||
"text/plain; charset=utf-8")
|
|
||||||
if etag != "" {
|
if etag != "" {
|
||||||
w.Header().Set("etag", etag)
|
w.Header().Set("etag", etag)
|
||||||
}
|
}
|
||||||
if r.Method == "HEAD" {
|
if r.Method == "HEAD" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, t := range tokens {
|
toknames := make([]string, len(tokens))
|
||||||
fmt.Fprintln(w, t.Token)
|
for i, t := range tokens {
|
||||||
|
toknames[i] = t.Token
|
||||||
}
|
}
|
||||||
|
e := json.NewEncoder(w)
|
||||||
|
e.Encode(toknames)
|
||||||
return
|
return
|
||||||
} else if r.Method == "POST" {
|
} else if r.Method == "POST" {
|
||||||
ctype := parseContentType(r.Header.Get("Content-Type"))
|
ctype := parseContentType(r.Header.Get("Content-Type"))
|
||||||
|
@ -586,8 +585,6 @@ func tokensHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("content-type",
|
|
||||||
"text/plain; charset=utf-8")
|
|
||||||
w.Header().Set("location", t.Token)
|
w.Header().Set("location", t.Token)
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
return
|
return
|
||||||
|
|
|
@ -3,7 +3,6 @@ package webserver
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -91,23 +90,6 @@ func TestApi(t *testing.T) {
|
||||||
return client.Do(req)
|
return client.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
getString := func(path string) (string, error) {
|
|
||||||
resp, err := do("GET", path, "", "", "", "")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return "", fmt.Errorf("Status is %v", resp.StatusCode)
|
|
||||||
}
|
|
||||||
ctype := parseContentType(resp.Header.Get("Content-Type"))
|
|
||||||
if !strings.EqualFold(ctype, "text/plain") {
|
|
||||||
return "", errors.New("Unexpected Content-Type")
|
|
||||||
}
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
|
||||||
return string(b), err
|
|
||||||
}
|
|
||||||
|
|
||||||
getJSON := func(path string, value any) error {
|
getJSON := func(path string, value any) error {
|
||||||
resp, err := do("GET", path, "", "", "", "")
|
resp, err := do("GET", path, "", "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -119,14 +101,15 @@ func TestApi(t *testing.T) {
|
||||||
}
|
}
|
||||||
ctype := parseContentType(resp.Header.Get("Content-Type"))
|
ctype := parseContentType(resp.Header.Get("Content-Type"))
|
||||||
if !strings.EqualFold(ctype, "application/json") {
|
if !strings.EqualFold(ctype, "application/json") {
|
||||||
return errors.New("Unexpected")
|
return errors.New("Unexpected content-type")
|
||||||
}
|
}
|
||||||
d := json.NewDecoder(resp.Body)
|
d := json.NewDecoder(resp.Body)
|
||||||
return d.Decode(value)
|
return d.Decode(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := getString("/galene-api/v0/.groups/")
|
var groups []string
|
||||||
if err != nil || s != "" {
|
err = getJSON("/galene-api/v0/.groups/", &groups)
|
||||||
|
if err != nil || len(groups) != 0 {
|
||||||
t.Errorf("Get groups: %v", err)
|
t.Errorf("Get groups: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,9 +154,9 @@ func TestApi(t *testing.T) {
|
||||||
t.Errorf("Delete group (bad ETag): %v %v", err, resp.StatusCode)
|
t.Errorf("Delete group (bad ETag): %v %v", err, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err = getString("/galene-api/v0/.groups/")
|
err = getJSON("/galene-api/v0/.groups/", &groups)
|
||||||
if err != nil || s != "test\n" {
|
if err != nil || len(groups) != 1 || groups[0] != "test" {
|
||||||
t.Errorf("Get groups: %v %#v", err, s)
|
t.Errorf("Get groups: %v %v", err, groups)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = do("PUT", "/galene-api/v0/.groups/test/.fallback-users",
|
resp, err = do("PUT", "/galene-api/v0/.groups/test/.fallback-users",
|
||||||
|
@ -193,8 +176,8 @@ func TestApi(t *testing.T) {
|
||||||
t.Errorf("Set key: %v %v", err, resp.StatusCode)
|
t.Errorf("Set key: %v %v", err, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err = getString("/galene-api/v0/.groups/test/.users/")
|
err = getJSON("/galene-api/v0/.groups/test/.users/", &groups)
|
||||||
if err != nil || s != "" {
|
if err != nil || len(groups) != 0 {
|
||||||
t.Errorf("Get users: %v", err)
|
t.Errorf("Get users: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,9 +196,10 @@ func TestApi(t *testing.T) {
|
||||||
t.Errorf("Create user: %v %v", err, resp.StatusCode)
|
t.Errorf("Create user: %v %v", err, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err = getString("/galene-api/v0/.groups/test/.users/")
|
var users []string
|
||||||
if err != nil || s != "jch\n" {
|
err = getJSON("/galene-api/v0/.groups/test/.users/", &users)
|
||||||
t.Errorf("Get users: %v", err)
|
if err != nil || len(users) != 1 || users[0] != "jch" {
|
||||||
|
t.Errorf("Get users: %v %v", err, users)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = do("PUT", "/galene-api/v0/.groups/test/.users/jch",
|
resp, err = do("PUT", "/galene-api/v0/.groups/test/.users/jch",
|
||||||
|
@ -296,11 +280,12 @@ func TestApi(t *testing.T) {
|
||||||
t.Errorf("Create token: %v %v", err, resp.StatusCode)
|
t.Errorf("Create token: %v %v", err, resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
tokname, err := getString("/galene-api/v0/.groups/test/.tokens/")
|
var toknames []string
|
||||||
if err != nil {
|
err = getJSON("/galene-api/v0/.groups/test/.tokens/", &toknames)
|
||||||
t.Errorf("Get tokens: %v", err)
|
if err != nil || len(toknames) != 1 {
|
||||||
|
t.Errorf("Get tokens: %v %v", err, toknames)
|
||||||
}
|
}
|
||||||
tokname = tokname[:len(tokname)-1]
|
tokname := toknames[0]
|
||||||
|
|
||||||
tokens, etag, err := token.List("test")
|
tokens, etag, err := token.List("test")
|
||||||
if err != nil || len(tokens) != 1 || tokens[0].Token != tokname {
|
if err != nil || len(tokens) != 1 || tokens[0].Token != tokname {
|
||||||
|
|
Loading…
Reference in a new issue