Skip to content

Commit

Permalink
Add GetDocuments API returning document summaries (#909)
Browse files Browse the repository at this point in the history
  • Loading branch information
hackerwins authored Jun 28, 2024
1 parent 4226417 commit 275cdff
Show file tree
Hide file tree
Showing 11 changed files with 1,357 additions and 903 deletions.
589 changes: 325 additions & 264 deletions api/docs/yorkie/v1/admin.openapi.yaml

Large diffs are not rendered by default.

409 changes: 204 additions & 205 deletions api/docs/yorkie/v1/resources.openapi.yaml

Large diffs are not rendered by default.

453 changes: 226 additions & 227 deletions api/docs/yorkie/v1/yorkie.openapi.yaml

Large diffs are not rendered by default.

558 changes: 353 additions & 205 deletions api/yorkie/v1/admin.pb.go

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions api/yorkie/v1/admin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ service AdminService {

rpc ListDocuments (ListDocumentsRequest) returns (ListDocumentsResponse) {}
rpc GetDocument (GetDocumentRequest) returns (GetDocumentResponse) {}
rpc GetDocuments (GetDocumentsRequest) returns (GetDocumentsResponse) {}
rpc RemoveDocumentByAdmin (RemoveDocumentByAdminRequest) returns (RemoveDocumentByAdminResponse) {}
rpc GetSnapshotMeta (GetSnapshotMetaRequest) returns (GetSnapshotMetaResponse) {}
rpc SearchDocuments (SearchDocumentsRequest) returns (SearchDocumentsResponse) {}
Expand Down Expand Up @@ -113,6 +114,15 @@ message GetDocumentResponse {
DocumentSummary document = 1;
}

message GetDocumentsRequest {
string project_name = 1;
repeated string document_keys = 2;
}

message GetDocumentsResponse {
repeated DocumentSummary documents = 1;
}

message RemoveDocumentByAdminRequest {
string project_name = 1;
string document_key = 2;
Expand Down
27 changes: 27 additions & 0 deletions api/yorkie/v1/v1connect/admin.connect.go

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

21 changes: 21 additions & 0 deletions server/documents/documents.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,27 @@ func GetDocumentSummary(
}, nil
}

// GetDocumentSummaries returns a list of document summaries.
func GetDocumentSummaries(
ctx context.Context,
b *backend.Backend,
project *types.Project,
keys []key.Key,
) ([]*types.DocumentSummary, error) {
// TODO(hackerwins): Resolve the N+1 problem.
var summaries []*types.DocumentSummary
for _, k := range keys {
summary, err := GetDocumentSummary(ctx, b, project, k)
if err != nil {
return nil, err
}

summaries = append(summaries, summary)
}

return summaries, nil
}

// GetDocumentByServerSeq returns a document for the given server sequence.
func GetDocumentByServerSeq(
ctx context.Context,
Expand Down
31 changes: 31 additions & 0 deletions server/rpc/admin_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,37 @@ func (s *adminServer) GetDocument(
}), nil
}

// GetDocuments gets the documents.
func (s *adminServer) GetDocuments(
ctx context.Context,
req *connect.Request[api.GetDocumentsRequest],
) (*connect.Response[api.GetDocumentsResponse], error) {
user := users.From(ctx)
project, err := projects.GetProject(ctx, s.backend, user.ID, req.Msg.ProjectName)
if err != nil {
return nil, err
}

var keys []key.Key
for _, k := range req.Msg.DocumentKeys {
keys = append(keys, key.Key(k))
}

docs, err := documents.GetDocumentSummaries(
ctx,
s.backend,
project,
keys,
)
if err != nil {
return nil, err
}

return connect.NewResponse(&api.GetDocumentsResponse{
Documents: converter.ToDocumentSummaries(docs),
}), nil
}

// GetSnapshotMeta gets the snapshot metadata that corresponds to the server sequence.
func (s *adminServer) GetSnapshotMeta(
ctx context.Context,
Expand Down
17 changes: 16 additions & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (r *Yorkie) RPCAddr() string {

// DeactivateClient deactivates the given client. It is used for testing.
func (r *Yorkie) DeactivateClient(ctx context.Context, c1 *client.Client) error {
project, err := projects.GetProjectFromAPIKey(ctx, r.backend, "")
project, err := r.DefaultProject(ctx)
if err != nil {
return err
}
Expand All @@ -147,3 +147,18 @@ func (r *Yorkie) DeactivateClient(ctx context.Context, c1 *client.Client) error
})
return err
}

// DefaultProject returns the default project.
func (r *Yorkie) DefaultProject(ctx context.Context) (*types.Project, error) {
return projects.GetProjectFromAPIKey(ctx, r.backend, "")
}

// CreateProject creates a project with the given name.
func (r *Yorkie) CreateProject(ctx context.Context, name string) (*types.Project, error) {
project, err := r.DefaultProject(ctx)
if err != nil {
return nil, err
}

return projects.CreateProject(ctx, r.backend, project.Owner, name)
}
7 changes: 6 additions & 1 deletion test/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,13 @@ func TestServer() *server.Yorkie {
}

// TestDocKey returns a new instance of document key for testing.
func TestDocKey(t testing.TB) key.Key {
func TestDocKey(t testing.TB, prefix ...int) key.Key {
name := t.Name()

if len(prefix) > 0 {
name = fmt.Sprintf("%d-%s", prefix[0], name)
}

if err := key.Key(name).Validate(); err == nil {
return key.Key(name)
}
Expand Down
138 changes: 138 additions & 0 deletions test/integration/restapi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//go:build integration

/*
* Copyright 2024 The Yorkie Authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package integration

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"testing"

"github.com/stretchr/testify/assert"

"github.com/yorkie-team/yorkie/api/types"
"github.com/yorkie-team/yorkie/client"
"github.com/yorkie-team/yorkie/pkg/document"
"github.com/yorkie-team/yorkie/test/helper"
)

// documentSummaries represents a list of document documentSummaries.
type documentSummaries struct {
Documents []*types.DocumentSummary `json:"documents"`
}

// documentSummary represents a summary of a document.
type documentSummary struct {
Document *types.DocumentSummary `json:"document"`
}

func TestRESTAPI(t *testing.T) {
project, docs := createProjectAndDocuments(t, 3)

t.Run("document retrieval test", func(t *testing.T) {
httpClient := http.Client{}

url := fmt.Sprintf("http://%s/yorkie.v1.AdminService/GetDocument", defaultServer.RPCAddr())
req, err := http.NewRequest("POST", url, strings.NewReader(
fmt.Sprintf(`{"project_name": "%s", "document_key": "%s"}`, project.Name, docs[0].Key()),
))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", project.SecretKey)

res, err := httpClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)

resBody, err := io.ReadAll(res.Body)
assert.NoError(t, err)

summary := &documentSummary{}
assert.NoError(t, json.Unmarshal(resBody, summary))
assert.Equal(t, docs[0].Key(), summary.Document.Key)
})

t.Run("bulk document retrieval test", func(t *testing.T) {
httpClient := http.Client{}

url := fmt.Sprintf("http://%s/yorkie.v1.AdminService/GetDocuments", defaultServer.RPCAddr())
req, err := http.NewRequest("POST", url, strings.NewReader(
fmt.Sprintf(`{"project_name": "%s", "document_keys": ["%s", "%s"]}`, project.Name, docs[0].Key(), docs[1].Key()),
))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", project.SecretKey)

res, err := httpClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)

resBody, err := io.ReadAll(res.Body)
assert.NoError(t, err)

summaries := &documentSummaries{}
assert.NoError(t, json.Unmarshal(resBody, summaries))
assert.Len(t, summaries.Documents, 2)
})

t.Run("list documents test", func(t *testing.T) {
httpClient := http.Client{}

url := fmt.Sprintf("http://%s/yorkie.v1.AdminService/ListDocuments", defaultServer.RPCAddr())
req, err := http.NewRequest("POST", url, strings.NewReader(
fmt.Sprintf(`{"project_name": "%s", "document_key": "test"}`, project.Name),
))
assert.NoError(t, err)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", project.SecretKey)

res, err := httpClient.Do(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)

resBody, err := io.ReadAll(res.Body)
assert.NoError(t, err)

summaries := &documentSummaries{}
assert.NoError(t, json.Unmarshal(resBody, summaries))
assert.Len(t, summaries.Documents, 3)
})
}

func createProjectAndDocuments(t *testing.T, count int) (*types.Project, []*document.Document) {
ctx := context.Background()
project, err := defaultServer.CreateProject(ctx, t.Name())
assert.NoError(t, err)

cli, err := client.Dial(defaultServer.RPCAddr(), client.WithAPIKey(project.PublicKey))
assert.NoError(t, err)
assert.NoError(t, cli.Activate(ctx))

var docs []*document.Document
for i := 0; i < count; i++ {
doc := document.New(helper.TestDocKey(t, i))
assert.NoError(t, cli.Attach(ctx, doc))
docs = append(docs, doc)
}

return project, docs
}

0 comments on commit 275cdff

Please sign in to comment.