Skip to content

Commit

Permalink
feat(server): implement public api for assets (#1277)
Browse files Browse the repository at this point in the history
* imp

* add e2e tests

* implement find asset files by asset ids

* test: update the e2e test

* refactor

* implement asset list ids

---------

Co-authored-by: ymk <81808708+yk-eukarya@users.noreply.github.com>
  • Loading branch information
nourbalaha and yk-eukarya authored Oct 24, 2024
1 parent e862be7 commit 2b7ddb3
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 17 deletions.
35 changes: 31 additions & 4 deletions server/e2e/publicapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,16 +203,42 @@ func TestPublicAPI(t *testing.T) {
"error": "not found",
})

e.GET("/api/p/{project}/assets", publicAPIProjectAlias).
Expect().
Status(http.StatusOK).
JSON().
IsEqual(map[string]any{
"hasMore": false,
"limit": 50,
"offset": 0,
"page": 1,
"results": []map[string]any{
map[string]any{
"id": publicAPIAsset1ID.String(),
"type": "asset",
"url": fmt.Sprintf("https://example.com/assets/%s/%s/aaa.zip", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
"contentType": "application/zip",
"files": []string{
fmt.Sprintf("https://example.com/assets/%s/%s/aaa/bbb.txt", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
fmt.Sprintf("https://example.com/assets/%s/%s/aaa/ccc.txt", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
},
},
},
"totalCount": 1,
})

e.GET("/api/p/{project}/assets/{assetid}", publicAPIProjectAlias, publicAPIAsset1ID).
Expect().
Status(http.StatusOK).
JSON().
IsEqual(map[string]any{
"type": "asset",
"id": publicAPIAsset1ID.String(),
"url": fmt.Sprintf("https://example.com/assets/%s/%s/aaa.zip", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
"type": "asset",
"id": publicAPIAsset1ID.String(),
"url": fmt.Sprintf("https://example.com/assets/%s/%s/aaa.zip", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
"contentType": "application/zip",
"files": []string{
fmt.Sprintf("https://example.com/assets/%s/%s/aaa/bbb.txt", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
fmt.Sprintf("https://example.com/assets/%s/%s/aaa/ccc.txt", publicAPIAssetUUID[:2], publicAPIAssetUUID[2:]),
},
})

Expand Down Expand Up @@ -354,7 +380,8 @@ func publicAPISeeder(ctx context.Context, r *repo.Container) error {

a := asset.New().ID(publicAPIAsset1ID).Project(p1.ID()).CreatedByUser(uid).Size(1).Thread(id.NewThreadID()).
FileName("aaa.zip").UUID(publicAPIAssetUUID).MustBuild()
af := asset.NewFile().Name("bbb.txt").Path("aaa/bbb.txt").Build()
c := []*asset.File{asset.NewFile().Name("bbb.txt").Path("aaa/bbb.txt").Build(), asset.NewFile().Name("ccc.txt").Path("aaa/ccc.txt").Build()}
af := asset.NewFile().Name("aaa.zip").Path("aaa.zip").ContentType("application/zip").Size(10).Children(c).Build()

fid := id.NewFieldID()
gst := schema.GeometryObjectSupportedTypeList{schema.GeometryObjectSupportedTypePoint, schema.GeometryObjectSupportedTypeLineString}
Expand Down
31 changes: 20 additions & 11 deletions server/internal/adapter/publicapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func GetController(ctx context.Context) *Controller {

func Echo(e *echo.Group) {
e.Use(middleware.CORS())
e.GET("/:project/:model", PublicApiItemList())
e.GET("/:project/:model", PublicApiItemOrAssetList())
e.GET("/:project/:model/:item", PublicApiItemOrAsset())
}

Expand All @@ -59,33 +59,42 @@ func PublicApiItemOrAsset() echo.HandlerFunc {
}
}

func PublicApiItemList() echo.HandlerFunc {
func PublicApiItemOrAssetList() echo.HandlerFunc {
return func(c echo.Context) error {
ctx := c.Request().Context()
ctrl := GetController(ctx)

mKey := c.Param("model")
pKey := c.Param("project")
p, err := listParamFromEchoContext(c)
if err != nil {
return c.JSON(http.StatusBadRequest, "invalid offset or limit")
}

if mKey == "assets" {
res, err := ctrl.GetAssets(ctx, pKey, p)
if err != nil {
return err
}
return c.JSON(http.StatusOK, res)
}

resType := ""
m := c.Param("model")
if strings.Contains(m, ".") {
m, resType, _ = strings.Cut(m, ".")
if strings.Contains(mKey, ".") {
mKey, resType, _ = strings.Cut(mKey, ".")
}
if resType != "csv" && resType != "json" && resType != "geojson" {
resType = "json"
}

items, _, err := ctrl.GetItems(ctx, c.Param("project"), m, p)
res, _, err := ctrl.GetItems(ctx, pKey, mKey, p)
if err != nil {
return err
}

vi, s, err1 := ctrl.GetVersionedItems(ctx, c.Param("project"), m, p)
if err1 != nil {
return err1
vi, s, err := ctrl.GetVersionedItems(ctx, pKey, mKey, p)
if err != nil {
return err
}

switch resType {
Expand All @@ -94,9 +103,9 @@ func PublicApiItemList() echo.HandlerFunc {
case "geojson":
return toGeoJSON(c, vi, s)
case "json":
return c.JSON(http.StatusOK, items)
return c.JSON(http.StatusOK, res)
default:
return c.JSON(http.StatusOK, items)
return c.JSON(http.StatusOK, res)
}
}
}
Expand Down
37 changes: 37 additions & 0 deletions server/internal/adapter/publicapi/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import (
"context"
"errors"

"github.com/reearth/reearth-cms/server/internal/usecase/interfaces"
"github.com/reearth/reearth-cms/server/pkg/asset"
"github.com/reearth/reearth-cms/server/pkg/id"
"github.com/reearth/reearth-cms/server/pkg/project"
"github.com/reearth/reearthx/rerror"
"github.com/reearth/reearthx/util"
)

func (c *Controller) GetAsset(ctx context.Context, prj, i string) (Asset, error) {
Expand Down Expand Up @@ -34,3 +38,36 @@ func (c *Controller) GetAsset(ctx context.Context, prj, i string) (Asset, error)

return NewAsset(a, f, c.assetUrlResolver), nil
}

func (c *Controller) GetAssets(ctx context.Context, pKey string, p ListParam) (ListResult[Asset], error) {
prj, err := c.checkProject(ctx, pKey)
if err != nil {
return ListResult[Asset]{}, err
}

if prj.Publication().Scope() != project.PublicationScopePublic || !prj.Publication().AssetPublic() {
return ListResult[Asset]{}, rerror.ErrNotFound
}

al, pi, err := c.usecases.Asset.FindByProject(ctx, prj.ID(), interfaces.AssetFilter{
Sort: nil,
Keyword: nil,
Pagination: p.Pagination,
}, nil)

if err != nil {
if errors.Is(err, rerror.ErrNotFound) {
return ListResult[Asset]{}, rerror.ErrNotFound
}
return ListResult[Asset]{}, err
}

fileMap, err := c.usecases.Asset.FindFilesByIDs(ctx, al.IDs(), nil)
if err != nil {
return ListResult[Asset]{}, err
}

return NewListResult(util.Map(al, func(a *asset.Asset) Asset {
return NewAsset(a, fileMap[a.ID()], c.assetUrlResolver)
}), pi, p.Pagination), nil
}
4 changes: 2 additions & 2 deletions server/internal/adapter/publicapi/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ func NewAsset(a *asset.Asset, f *asset.File, urlResolver asset.URLResolver) Asse
base, _ := url.Parse(u)
base.Path = path.Dir(base.Path)

files = lo.Map(f.FlattenChildren(), func(f *asset.File, _ int) string {
files = lo.Map(f.FilePaths(), func(p string, _ int) string {
b := *base
b.Path = path.Join(b.Path, f.Path())
b.Path = path.Join(b.Path, p)
return b.String()
})
}
Expand Down
26 changes: 26 additions & 0 deletions server/internal/infrastructure/memory/asset_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,32 @@ func (r *AssetFile) FindByID(ctx context.Context, id id.AssetID) (*asset.File, e
return rerror.ErrIfNil(f, rerror.ErrNotFound)
}

func (r *AssetFile) FindByIDs(ctx context.Context, ids id.AssetIDList) (map[id.AssetID]*asset.File, error) {
if r.err != nil {
return nil, r.err
}

filesMap := make(map[id.AssetID]*asset.File)
for _, id := range ids {
f := r.data.Find(func(key asset.ID, value *asset.File) bool {
return key == id
}).Clone()
fs := r.files.Find(func(key asset.ID, value []*asset.File) bool {
return key == id
})
if len(fs) > 0 {
f.SetFiles(fs)
}
if f == nil {
return nil, rerror.ErrNotFound
}
filesMap[id] = f
}

return filesMap, nil
}


func (r *AssetFile) Save(ctx context.Context, id id.AssetID, file *asset.File) error {
if r.err != nil {
return r.err
Expand Down
46 changes: 46 additions & 0 deletions server/internal/infrastructure/mongo/asset_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,52 @@ func (r *AssetFile) FindByID(ctx context.Context, id id.AssetID) (*asset.File, e
return f, nil
}

func (r *AssetFile) FindByIDs(ctx context.Context, ids id.AssetIDList) (map[id.AssetID]*asset.File, error) {
filesMap := make(map[id.AssetID]*asset.File)

c := &mongodoc.AssetAndFileConsumer{}
if err := r.client.Find(ctx, bson.M{
"id": bson.M{"$in": ids.Strings()},
}, c, options.Find().SetProjection(bson.M{
"id": 1,
"file": 1,
"flatfiles": 1,
})); err != nil {
return nil, err
}

for _, result := range c.Result {
assetID := result.ID
f := result.File.Model()
if f == nil {
return nil, rerror.ErrNotFound
}

if result.FlatFiles {
var afc mongodoc.AssetFilesConsumer
if err := r.assetFilesClient.Find(ctx, bson.M{
"assetid": assetID,
}, &afc, options.Find().SetSort(bson.D{
{Key: "page", Value: 1},
})); err != nil {
return nil, err
}
files := afc.Result().Model()
f.SetFiles(files)
} else if len(f.Children()) > 0 {
f.SetFiles(f.FlattenChildren())
}

aId, err := id.AssetIDFrom(assetID)
if err != nil {
return nil, err
}
filesMap[aId] = f
}

return filesMap, nil
}

func (r *AssetFile) Save(ctx context.Context, id id.AssetID, file *asset.File) error {
doc := mongodoc.NewFile(file)
_, err := r.client.Client().UpdateOne(ctx, bson.M{
Expand Down
14 changes: 14 additions & 0 deletions server/internal/usecase/interactor/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ func (i *Asset) FindFileByID(ctx context.Context, aid id.AssetID, _ *usecase.Ope
return files, nil
}

func (i *Asset) FindFilesByIDs(ctx context.Context, ids id.AssetIDList, _ *usecase.Operator) (map[id.AssetID]*asset.File, error) {
_, err := i.repos.Asset.FindByIDs(ctx, ids)
if err != nil {
return nil, err
}

files, err := i.repos.AssetFile.FindByIDs(ctx, ids)
if err != nil {
return nil, err
}

return files, nil
}

func (i *Asset) DownloadByID(ctx context.Context, aid id.AssetID, _ *usecase.Operator) (io.ReadCloser, error) {
a, err := i.repos.Asset.FindByID(ctx, aid)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions server/internal/usecase/interfaces/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type Asset interface {
FindByIDs(context.Context, []id.AssetID, *usecase.Operator) (asset.List, error)
FindByProject(context.Context, id.ProjectID, AssetFilter, *usecase.Operator) (asset.List, *usecasex.PageInfo, error)
FindFileByID(context.Context, id.AssetID, *usecase.Operator) (*asset.File, error)
FindFilesByIDs(context.Context, id.AssetIDList, *usecase.Operator) (map[id.AssetID]*asset.File, error)
DownloadByID(context.Context, id.AssetID, *usecase.Operator) (io.ReadCloser, error)
GetURL(*asset.Asset) string
Create(context.Context, CreateAssetParam, *usecase.Operator) (*asset.Asset, *asset.File, error)
Expand Down
1 change: 1 addition & 0 deletions server/internal/usecase/repo/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Asset interface {

type AssetFile interface {
FindByID(context.Context, id.AssetID) (*asset.File, error)
FindByIDs(context.Context, id.AssetIDList) (map[id.AssetID]*asset.File, error)
Save(context.Context, id.AssetID, *asset.File) error
SaveFlat(context.Context, id.AssetID, *asset.File, []*asset.File) error
}
8 changes: 8 additions & 0 deletions server/pkg/asset/list.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package asset

import (
"github.com/reearth/reearth-cms/server/pkg/id"
"github.com/reearth/reearthx/util"
"github.com/samber/lo"
"golang.org/x/exp/slices"
Expand All @@ -27,3 +28,10 @@ func (l List) Map() Map {
return a.ID(), a
})
}

func (l List) IDs() (ids id.AssetIDList) {
for _, a := range l {
ids = ids.Add(a.ID())
}
return
}
10 changes: 10 additions & 0 deletions server/pkg/asset/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package asset
import (
"testing"

"github.com/reearth/reearth-cms/server/pkg/id"
"github.com/reearth/reearthx/account/accountdomain"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -51,3 +52,12 @@ func TestList_Map(t *testing.T) {
}, List{a, nil}.Map())
assert.Equal(t, Map{}, List(nil).Map())
}

func TestList_IDs(t *testing.T) {
pid := NewProjectID()
uid := accountdomain.NewUserID()
a1 := New().NewID().Project(pid).CreatedByUser(uid).Size(1000).Thread(NewThreadID()).NewUUID().MustBuild()
a2 := New().NewID().Project(pid).CreatedByUser(uid).Size(1000).Thread(NewThreadID()).NewUUID().MustBuild()
al := List{a1, a2}
assert.Equal(t, al.IDs(), id.AssetIDList{a1.ID(), a2.ID()})
}

0 comments on commit 2b7ddb3

Please sign in to comment.