diff --git a/README.md b/README.md index f57d702..aca0e94 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ The caller is expected to verify that they have write access to the specified di Once this is done, we call the `/register/finish` endpoint with a JSON-encoded request body that contains the same directory path in `path`. The body may also contain `base`, an array of strings containing the names of the metadata files in the directory to be indexed. -If `base` is not provided, only files named `metadata.json` will be indexed. +If `base` is not provided and `path` has already been registered, the `base` associated with `path`'s prior registration is re-used; +otherwise, if `path` was not previously registered, only files named `metadata.json` will be indexed. ```shell curl -X POST -L ${SEWER_RAT_URL}/register/finish \ diff --git a/database.go b/database.go index 02eb42e..43525e3 100644 --- a/database.go +++ b/database.go @@ -883,3 +883,26 @@ func isDirectoryRegistered(db * sql.DB, path string) (bool, error) { } return num > 0, nil } + +/**********************************************************************/ + +func fetchRegisteredDirectoryNames(db *sql.DB, path string) ([]string, error) { + row := db.QueryRow("SELECT json_extract(names, '$') FROM dirs WHERE path = ?", path) + var names_as_str string + err := row.Scan(&names_as_str) + + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } else if err != nil { + return nil, fmt.Errorf("failed to extract existing names for %q; %w", path, err) + } + + output := []string{} + err = json.Unmarshal([]byte(names_as_str), &output) + if err != nil { + return nil, fmt.Errorf("failed to parse existing names for %q; %w", path, err) + } + + return output, nil +} + diff --git a/database_test.go b/database_test.go index 62a9c3f..215ae5e 100644 --- a/database_test.go +++ b/database_test.go @@ -1946,3 +1946,47 @@ func TestIsDirectoryRegistered(t *testing.T) { } }) } + +func TestFetchRegisteredDirectoryNames(t *testing.T) { + tmp, err := os.MkdirTemp("", "") + if err != nil { + t.Fatalf(err.Error()) + } + defer os.RemoveAll(tmp) + + dbpath := filepath.Join(tmp, "db.sqlite3") + dbconn, err := initializeDatabase(dbpath) + if err != nil { + t.Fatalf(err.Error()) + } + defer dbconn.Close() + + tokr, err := newUnicodeTokenizer(false) + if err != nil { + t.Fatalf(err.Error()) + } + + to_add := filepath.Join(tmp, "liella") + err = mockDirectory(to_add) + if err != nil { + t.Fatalf(err.Error()) + } + + comments, err := addNewDirectory(dbconn, to_add, []string{ "metadata.json", "other.json" }, "aaron", tokr) + if err != nil { + t.Fatalf(err.Error()) + } + if len(comments) > 0 { + t.Fatalf("unexpected comments from the directory addition %v", comments) + } + + out, err := fetchRegisteredDirectoryNames(dbconn, to_add) + if len(out) != 2 || out[0] != "metadata.json" || out[1] != "other.json" { + t.Fatalf("unexpected names for the registered directory") + } + + out, err = fetchRegisteredDirectoryNames(dbconn, filepath.Join(tmp, "margarete")) + if len(out) != 0 { + t.Fatalf("unexpected names for a non-registered directory") + } +} diff --git a/handlers.go b/handlers.go index cc98ba2..ebb23b3 100644 --- a/handlers.go +++ b/handlers.go @@ -215,7 +215,16 @@ func newRegisterFinishHandler(db *sql.DB, verifier *verificationRegistry, tokeni return } } else { - allowed = []string{ "metadata.json" } + existing, err := fetchRegisteredDirectoryNames(db, output.Path) + if err != nil { + dumpHttpErrorResponse(w, err) + return + } + if existing == nil { + allowed = []string{ "metadata.json" } + } else { + allowed = existing + } } code_info, err := checkVerificationCode(regpath, verifier, timeout) diff --git a/handlers_test.go b/handlers_test.go index d80b3cb..f508a81 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -366,6 +366,76 @@ func TestRegisterHandlers(t *testing.T) { t.Fatalf("unexpected paths in the database %v", all_paths) } }) + + t.Run("register finish reuse names", func(t *testing.T) { + { + code := quickRegisterStart() + err := os.WriteFile(filepath.Join(to_add, code), []byte(""), 0644) + if err != nil { + t.Fatal(err) + } + + handler := http.HandlerFunc(newRegisterFinishHandler(dbconn, verifier, tokr, 1)) + rr := httptest.NewRecorder() + req := createJsonRequest("POST", "/register/finish", map[string]interface{}{ "path": to_add, "base": []string{ "alpha.json", "bravo.json" } }, t) + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusOK { + t.Fatalf("should have succeeded") + } + } + + // Re-registering with the same names. + { + code := quickRegisterStart() + err := os.WriteFile(filepath.Join(to_add, code), []byte(""), 0644) + if err != nil { + t.Fatal(err) + } + + handler := http.HandlerFunc(newRegisterFinishHandler(dbconn, verifier, tokr, 1)) + rr := httptest.NewRecorder() + req := createJsonRequest("POST", "/register/finish", map[string]interface{}{ "path": to_add }, t) + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusOK { + t.Fatalf("should have succeeded") + } + + all_paths, err := listDirs(dbconn) + if err != nil { + t.Fatal(err) + } + my_names, ok := all_paths[to_add] + if !ok || !equalStringArrays(my_names, []string{ "alpha.json", "bravo.json" }) { + t.Fatalf("unexpected paths in the database %v", my_names) + } + } + + // Re-registering with different names, to check that it indeed gets overridden. + { + code := quickRegisterStart() + err := os.WriteFile(filepath.Join(to_add, code), []byte(""), 0644) + if err != nil { + t.Fatal(err) + } + + handler := http.HandlerFunc(newRegisterFinishHandler(dbconn, verifier, tokr, 1)) + rr := httptest.NewRecorder() + req := createJsonRequest("POST", "/register/finish", map[string]interface{}{ "path": to_add, "base": []string{ "metadata.json" } }, t) + handler.ServeHTTP(rr, req) + if rr.Code != http.StatusOK { + t.Fatalf("should have succeeded") + } + + all_paths, err := listDirs(dbconn) + if err != nil { + t.Fatal(err) + } + my_names, ok := all_paths[to_add] + if !ok || !equalStringArrays(my_names, []string{ "metadata.json" }) { + t.Fatalf("unexpected paths in the database %v", my_names) + } + } + }) } func TestDeregisterHandlers(t *testing.T) {