Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(asset): implement file system repository and zip decompressor #66

Open
wants to merge 60 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
5f1b183
feat(asset): implement file system repository and zip decompressor
kasugamirai Dec 21, 2024
7ea0393
chore: add .idea to .gitignore
kasugamirai Dec 21, 2024
db64520
feat(asset): enhance asset management with status tracking and zip ex…
kasugamirai Dec 27, 2024
11e654a
feat(asset): implement CRUD operations for asset management
kasugamirai Dec 27, 2024
8ad88c8
refactor(asset): restructure repository interfaces for improved asset…
kasugamirai Dec 27, 2024
61b504b
feat(asset): enhance zip decompression with improved error handling a…
kasugamirai Dec 27, 2024
c3743ed
feat(asset): refactor ZipDecompressor for improved async processing a…
kasugamirai Dec 27, 2024
bb09b06
refactor(asset): remove legacy GCS repository implementation
kasugamirai Dec 27, 2024
60b401e
refactor(asset): rename Repository to GCS and update methods for impr…
kasugamirai Jan 3, 2025
9d62ead
refactor(asset): remove GCS repository implementation to streamline a…
kasugamirai Jan 3, 2025
8faee9e
refactor(asset): remove repository interface and update import paths
kasugamirai Jan 3, 2025
c4b9036
refactor(asset): rename GCSClient to Client and update methods for co…
kasugamirai Jan 3, 2025
ce054b9
refactor(asset): remove decompressor and GCS implementations, update …
kasugamirai Jan 5, 2025
375edef
refactor(asset): enhance ZipDecompressor with new methods and improve…
kasugamirai Jan 5, 2025
2412515
feat(asset): implement asynchronous and direct content compression in…
kasugamirai Jan 5, 2025
1d356a9
refactor(asset): update NewZipDecompressor return type for improved i…
kasugamirai Jan 5, 2025
c3717b2
refactor(asset): remove ZipDecompressor implementation to streamline …
kasugamirai Jan 5, 2025
3957926
refactor(asset): simplify ZipDecompressor and enhance decompression f…
kasugamirai Jan 5, 2025
0645e35
refactor(asset): enhance CompressWithContent for improved concurrency…
kasugamirai Jan 5, 2025
c0b0f15
feat(asset): enhance GCS client with new asset management methods
kasugamirai Jan 5, 2025
15af103
feat(asset): add projectID and workspaceID to Asset struct
kasugamirai Jan 6, 2025
7824b48
refactor(gcs): remove redundant name assignment in CRUD tests
kasugamirai Jan 6, 2025
9a4f777
refactor(asset): improve error handling and logging in ZipDecompresso…
kasugamirai Jan 6, 2025
129d3f0
refactor(asset): remove deprecated repository and asset types to stre…
kasugamirai Jan 6, 2025
667821d
refactor(asset): comment out service methods and remove utils for cod…
kasugamirai Jan 6, 2025
afeaaa3
refactor(gcs): remove GCS client test file to streamline codebase
kasugamirai Jan 6, 2025
acbcaad
refactor(asset): remove asset service implementation to streamline co…
kasugamirai Jan 6, 2025
3f2bc9c
feat(asset): implement asset management queries and mutations
kasugamirai Jan 6, 2025
fd1e4b6
feat(asset): implement asset movement and deletion functionality
kasugamirai Jan 6, 2025
6e99c4a
feat(asset): enhance asset management with new fields and methods
kasugamirai Jan 7, 2025
1248cc6
feat(asset): add decompression and compression methods to asset service
kasugamirai Jan 7, 2025
5375f2f
feat(asset): refactor compression to use channels for asynchronous pr…
kasugamirai Jan 7, 2025
460e01f
refactor(asset): improve zip decompression concurrency and error hand…
kasugamirai Jan 7, 2025
7352cb9
feat(asset): enhance asset event handling with new event types and me…
kasugamirai Jan 7, 2025
4078a4c
refactor(asset): remove pubsub implementation to streamline codebase
kasugamirai Jan 7, 2025
82d9d8e
feat(asset): integrate pubsub functionality for asset event publishing
kasugamirai Jan 7, 2025
4185ab4
feat(asset): reintroduce pubsub functionality for asset event handling
kasugamirai Jan 7, 2025
f8f8301
feat(asset): enhance pubsub functionality with subscription and event…
kasugamirai Jan 7, 2025
17bae14
fix(account): improve error handling in user credential retrieval and…
kasugamirai Jan 7, 2025
29326af
feat(account): add test for asset
kasugamirai Jan 9, 2025
abbb723
refactor(asset): streamline asset event handling and improve error ma…
kasugamirai Jan 9, 2025
4d828d5
feat(asset): add deleteAssetsInGroup mutation for bulk asset deletion
kasugamirai Jan 9, 2025
1428f87
refactor(asset): rename Builder to AssetBuilder and update related tests
kasugamirai Jan 9, 2025
7af2ba8
chore(asset): remove group service implementation and related tests
kasugamirai Jan 9, 2025
dd556f2
feat(asset): add SetSize method and refactor resolver to use asset us…
kasugamirai Jan 9, 2025
6d890f9
refactor(asset): remove unused asset usecase and interactor implement…
kasugamirai Jan 12, 2025
8b3514b
refactor(asset): update import path for asset usecase in resolver
kasugamirai Jan 12, 2025
9798ad9
refactor(asset): migrate ID types to new id package and clean up doma…
kasugamirai Jan 12, 2025
afda58a
refactor(asset): remove deprecated domain files and update references…
kasugamirai Jan 13, 2025
39b0572
feat(asset): implement validation for Asset and Group entities
kasugamirai Jan 13, 2025
4abf753
feat(asset): enhance asset usecase and resolver error handling
kasugamirai Jan 13, 2025
ae39bf0
fix(asset): improve error handling in builders and tests
kasugamirai Jan 13, 2025
44df313
refactor(asset): remove unused repository interface and clean up vali…
kasugamirai Jan 13, 2025
ede3ff9
refactor(asset): streamline CreatedAt methods in builders
kasugamirai Jan 13, 2025
c545a1a
refactor(asset): remove event package and clean up builder error hand…
kasugamirai Jan 13, 2025
ebcfc72
Revert
kasugamirai Jan 13, 2025
b88d638
refactor(asset): simplify loop syntax in DecompressZipContent method
kasugamirai Jan 13, 2025
ae883ae
refactor(asset): improve asset and group entity handling and error ma…
kasugamirai Jan 21, 2025
f06a63e
refactor(asset): update ID methods and improve error handling in deco…
kasugamirai Jan 26, 2025
9124c84
refactor(asset): remove unused NewValidationResult function
kasugamirai Jan 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.env
.env.*
.idea
114 changes: 114 additions & 0 deletions asset/domain/builder/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package builder

import (
"time"

"github.com/reearth/reearthx/asset/domain"
"github.com/reearth/reearthx/asset/domain/entity"
"github.com/reearth/reearthx/asset/domain/id"
)

type AssetBuilder struct {
a *entity.Asset
}

func NewAssetBuilder() *AssetBuilder {
return &AssetBuilder{a: &entity.Asset{}}
}

func (b *AssetBuilder) Build() (*entity.Asset, error) {
if b.a.ID() == (id.ID{}) {
return nil, id.ErrInvalidID
}
if b.a.WorkspaceID() == (id.WorkspaceID{}) {
return nil, domain.ErrEmptyWorkspaceID
}
if b.a.URL() == "" {
return nil, domain.ErrEmptyURL
}
if b.a.Size() <= 0 {
return nil, domain.ErrEmptySize
}
if b.a.CreatedAt().IsZero() {
now := time.Now()
b.a.SetCreatedAt(now)
b.a.SetUpdatedAt(now)
}
if b.a.Status() == "" {
b.a.UpdateStatus(entity.StatusPending, b.a.Error())
}
return b.a, nil
}

func (b *AssetBuilder) MustBuild() *entity.Asset {
r, err := b.Build()
if err != nil {
panic(err)
}
return r
}

func (b *AssetBuilder) ID(id id.ID) *AssetBuilder {
b.a = entity.NewAsset(id, b.a.Name(), b.a.Size(), b.a.ContentType())
return b
}
Comment on lines +51 to +54
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid re-initializing the asset in the ID method

The ID method re-initializes the Asset instance using entity.NewAsset, which can overwrite fields previously set by other builder methods, potentially leading to data loss. Instead, consider updating only the id field of the existing asset to preserve all other fields.

Since the id field in entity.Asset is unexported and there's no SetID method, you might need to modify the entity.Asset struct to allow setting the id field. One approach is to add a SetID method to entity.Asset. Here's how you could adjust the ID method:

-func (b *AssetBuilder) ID(id id.ID) *AssetBuilder {
-	b.a = entity.NewAsset(id, b.a.Name(), b.a.Size(), b.a.ContentType())
+func (b *AssetBuilder) ID(id id.ID) *AssetBuilder {
+	b.a.SetID(id)
 	return b
 }

Ensure that you implement SetID within entity.Asset:

// In entity/asset.go
func (a *Asset) SetID(id id.ID) {
	a.id = id
}


func (b *AssetBuilder) NewID() *AssetBuilder {
return b.ID(id.NewID())
}

func (b *AssetBuilder) GroupID(groupID id.GroupID) *AssetBuilder {
b.a.MoveToGroup(groupID)
return b
}

func (b *AssetBuilder) ProjectID(projectID id.ProjectID) *AssetBuilder {
b.a.MoveToProject(projectID)
return b
}

func (b *AssetBuilder) WorkspaceID(workspaceID id.WorkspaceID) *AssetBuilder {
b.a.MoveToWorkspace(workspaceID)
return b
}

func (b *AssetBuilder) Name(name string) *AssetBuilder {
b.a.UpdateMetadata(name, b.a.URL(), b.a.ContentType())
return b
}

func (b *AssetBuilder) Size(size int64) *AssetBuilder {
b.a.SetSize(size)
return b
}

func (b *AssetBuilder) URL(url string) *AssetBuilder {
b.a.UpdateMetadata(b.a.Name(), url, b.a.ContentType())
return b
}

func (b *AssetBuilder) ContentType(contentType string) *AssetBuilder {
b.a.UpdateMetadata(b.a.Name(), b.a.URL(), contentType)
return b
}

func (b *AssetBuilder) Status(status entity.Status) *AssetBuilder {
b.a.UpdateStatus(status, b.a.Error())
return b
}

func (b *AssetBuilder) Error(err string) *AssetBuilder {
b.a.UpdateStatus(b.a.Status(), err)
return b
}

// CreatedAt sets the creation time of the asset
func (b *AssetBuilder) CreatedAt(createdAt time.Time) *AssetBuilder {
b.a.SetCreatedAt(createdAt)
return b
}

func (b *AssetBuilder) UpdatedAt(updatedAt time.Time) *AssetBuilder {
b.a.SetUpdatedAt(updatedAt)
return b
}
79 changes: 79 additions & 0 deletions asset/domain/builder/group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package builder

import (
"time"

"github.com/reearth/reearthx/asset/domain"
"github.com/reearth/reearthx/asset/domain/entity"
"github.com/reearth/reearthx/asset/domain/id"
)

type GroupBuilder struct {
g *entity.Group
}

func NewGroupBuilder() *GroupBuilder {
return &GroupBuilder{g: &entity.Group{}}
}

func (b *GroupBuilder) Build() (*entity.Group, error) {
if b.g.ID() == (id.GroupID{}) {
return nil, id.ErrInvalidID
}
if b.g.Name() == "" {
return nil, domain.ErrEmptyGroupName
}
if b.g.CreatedAt().IsZero() {
now := time.Now()
b.CreatedAt(now)
}
return b.g, nil
}

func (b *GroupBuilder) MustBuild() *entity.Group {
r, err := b.Build()
if err != nil {
panic(err)
}
return r
}

func (b *GroupBuilder) ID(id id.GroupID) *GroupBuilder {
b.g = entity.NewGroup(id, b.g.Name())
return b
}

func (b *GroupBuilder) NewID() *GroupBuilder {
return b.ID(id.NewGroupID())
}

func (b *GroupBuilder) Name(name string) *GroupBuilder {
if err := b.g.UpdateName(name); err != nil {
return b
}
return b
}

func (b *GroupBuilder) Policy(policy string) *GroupBuilder {
if err := b.g.UpdatePolicy(policy); err != nil {
return b
}
return b
}

func (b *GroupBuilder) Description(description string) *GroupBuilder {
if err := b.g.UpdateDescription(description); err != nil {
return b
}
return b
}

// CreatedAt sets the creation time of the group
func (b *GroupBuilder) CreatedAt(createdAt time.Time) *GroupBuilder {
b.g.SetCreatedAt(createdAt)
return b
}

func (b *GroupBuilder) UpdatedAt(updatedAt time.Time) *GroupBuilder {
return b
}
190 changes: 190 additions & 0 deletions asset/domain/builder/tests/asset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package builder_test

import (
"testing"
"time"

"github.com/reearth/reearthx/asset/domain"
"github.com/reearth/reearthx/asset/domain/builder"
"github.com/reearth/reearthx/asset/domain/entity"
"github.com/reearth/reearthx/asset/domain/id"
"github.com/stretchr/testify/assert"
)

func TestAssetBuilder_Build(t *testing.T) {
assetID := id.NewID()
workspaceID := id.NewWorkspaceID()

tests := []struct {
name string
builder func() *builder.AssetBuilder
want *entity.Asset
wantErr error
}{
{
name: "success",
builder: func() *builder.AssetBuilder {
return builder.NewAssetBuilder().
ID(assetID).
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
WorkspaceID(workspaceID).
URL("https://example.com/test.jpg")
},
want: func() *entity.Asset {
asset := entity.NewAsset(assetID, "test.jpg", 1024, "image/jpeg")
asset.MoveToWorkspace(workspaceID)
asset.UpdateMetadata("test.jpg", "https://example.com/test.jpg", "image/jpeg")
return asset
}(),
wantErr: nil,
},
{
name: "missing ID",
builder: func() *builder.AssetBuilder {
return builder.NewAssetBuilder().
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
WorkspaceID(workspaceID).
URL("https://example.com/test.jpg")
},
wantErr: id.ErrInvalidID,
},
{
name: "missing workspace ID",
builder: func() *builder.AssetBuilder {
return builder.NewAssetBuilder().
ID(assetID).
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
URL("https://example.com/test.jpg")
},
wantErr: domain.ErrEmptyWorkspaceID,
},
{
name: "missing URL",
builder: func() *builder.AssetBuilder {
return builder.NewAssetBuilder().
ID(assetID).
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
WorkspaceID(workspaceID)
},
wantErr: domain.ErrEmptyURL,
},
{
name: "invalid size",
builder: func() *builder.AssetBuilder {
return builder.NewAssetBuilder().
ID(assetID).
Name("test.jpg").
Size(0).
ContentType("image/jpeg").
WorkspaceID(workspaceID).
URL("https://example.com/test.jpg")
},
wantErr: domain.ErrEmptySize,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.builder().Build()
if tt.wantErr != nil {
assert.Equal(t, tt.wantErr, err)
assert.Nil(t, got)
return
}
assert.NoError(t, err)
assert.NotNil(t, got)
if tt.want != nil {
assert.Equal(t, tt.want.ID(), got.ID())
assert.Equal(t, tt.want.Name(), got.Name())
assert.Equal(t, tt.want.Size(), got.Size())
assert.Equal(t, tt.want.ContentType(), got.ContentType())
assert.Equal(t, tt.want.URL(), got.URL())
assert.Equal(t, tt.want.WorkspaceID(), got.WorkspaceID())
}
})
}
}

func TestAssetBuilder_MustBuild(t *testing.T) {
assetID := id.NewID()
workspaceID := id.NewWorkspaceID()

// Test successful build
assert.NotPanics(t, func() {
asset := builder.NewAssetBuilder().
ID(assetID).
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
WorkspaceID(workspaceID).
URL("https://example.com/test.jpg").
MustBuild()
assert.NotNil(t, asset)
})

// Test panic on invalid build
assert.Panics(t, func() {
_ = builder.NewAssetBuilder().MustBuild()
})
}

func TestAssetBuilder_Setters(t *testing.T) {
assetID := id.NewID()
workspaceID := id.NewWorkspaceID()
projectID := id.NewProjectID()
groupID := id.NewGroupID()
now := time.Now()

b := builder.NewAssetBuilder().
CreatedAt(now).
ID(assetID).
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
WorkspaceID(workspaceID).
ProjectID(projectID).
GroupID(groupID).
URL("https://example.com/test.jpg").
Status(entity.StatusActive).
Error("test error")

asset, err := b.Build()
assert.NoError(t, err)
assert.NotNil(t, asset)

assert.Equal(t, assetID, asset.ID())
assert.Equal(t, workspaceID, asset.WorkspaceID())
assert.Equal(t, projectID, asset.ProjectID())
assert.Equal(t, groupID, asset.GroupID())
assert.Equal(t, "test.jpg", asset.Name())
assert.Equal(t, int64(1024), asset.Size())
assert.Equal(t, "https://example.com/test.jpg", asset.URL())
assert.Equal(t, "image/jpeg", asset.ContentType())
assert.Equal(t, entity.StatusActive, asset.Status())
assert.Equal(t, "test error", asset.Error())
assert.Equal(t, now.Unix(), asset.CreatedAt().Unix())
}

func TestAssetBuilder_NewID(t *testing.T) {
b := builder.NewAssetBuilder().NewID()
// Add required fields to make the build succeed
b = b.
Name("test.jpg").
Size(1024).
ContentType("image/jpeg").
WorkspaceID(id.NewWorkspaceID()).
URL("https://example.com/test.jpg")

asset, err := b.Build()
assert.NoError(t, err)
assert.NotNil(t, asset)
assert.NotEqual(t, id.ID{}, asset.ID()) // ID should be set
}
Loading
Loading