-
Notifications
You must be signed in to change notification settings - Fork 47
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(server): associate project asset #1410
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis pull request introduces extensive modifications across multiple parts of the codebase. Notable changes include the addition of a new Makefile target for resetting services, significant updates in end-to-end tests and utility functions, and widespread removals and refactoring in GraphQL schemas, resolvers, data loaders, and conversion functions. New project ID parameters have been added to asset-related functionality while dataset, tag, cluster, and infobox management components have been removed or simplified. Infrastructure and use case layers have been updated to support these changes, and similar adjustments are reflected in the web API files. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant R as GraphQL Resolver
participant I as Asset Interactor
participant Repo as Asset Repository
participant DB as Database (MongoDB)
C->>R: CreateAsset(input with optional projectId)
R->>I: Call CreateAsset with projectID and asset details
I->>Repo: Upload asset file and create asset record\n(with project association)
Repo->>DB: Insert asset document (includes Project field)
DB-->>Repo: Confirmation
Repo-->>I: Return created asset details
I-->>R: Pass asset details back
R-->>C: Respond with Asset Created result
Suggested reviewers
Poem
Tip 🌐 Web search-backed reviews and chat
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
✅ Deploy Preview for reearth-web canceled.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🔭 Outside diff range comments (2)
web/src/beta/features/AssetsManager/hooks.ts (2)
184-184
: Pass the projectId parameter to FindByWorkspace.The
projectId
parameter should be passed to theFindByWorkspace
function to enable filtering assets by project.Apply this diff to fix the issue:
- assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), nil, nil, sort, page, getOperator(ctx)) + assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), &projectId, nil, sort, page, getOperator(ctx))
202-213
: Add missing dependency to useCallback hook.The
projectId
parameter is used within the callback but is missing from the dependency array.Apply this diff to fix the issue:
const handleAssetsCreate = useCallback( async (files?: FileList) => { if (!files) return; await useCreateAssets({ teamId: workspaceId ?? "", projectId: projectId ?? "", file: files, coreSupport: true }); }, - [workspaceId, useCreateAssets] + [workspaceId, projectId, useCreateAssets] );🧰 Tools
🪛 GitHub Check: ci-web / ci
[failure] 212-212:
React Hook useCallback has a missing dependency: 'projectId'. Either include it or remove the dependency array🪛 GitHub Actions: ci
[error] 212-212: React Hook useCallback has a missing dependency: 'projectId'. Either include it or remove the dependency array.
🧹 Nitpick comments (12)
server/pkg/scene/builder/builder.go (2)
46-55
: Consider documenting the exporter configuration.The exporter setup is crucial for layer processing but lacks documentation about its role and configuration. Consider adding comments explaining:
- The merger's role in layer/property loading
- The sealer's role in dataset/tag processing
133-159
: Improve error message consistency and clarity.The error messages in BuildResult could be more descriptive and consistent:
- Error prefixes vary between "Fail" and "Fail build"
- Error messages use inconsistent separators
Consider applying this diff to improve error messages:
- return nil, errors.New("Fail buildScene :" + err.Error()) + return nil, fmt.Errorf("failed to build scene: %w", err) - return nil, errors.New("Fail buildStory :" + err.Error()) + return nil, fmt.Errorf("failed to build story: %w", err) - return nil, errors.New("Fail buildNLSLayers :" + err.Error()) + return nil, fmt.Errorf("failed to build NLS layers: %w", err) - return nil, errors.New("Fail buildLayerStyles :" + err.Error()) + return nil, fmt.Errorf("failed to build layer styles: %w", err)Also add the following import:
+ "fmt"
server/e2e/gql_asset_test.go (2)
29-83
: Repeated JSON checks hint at code duplication.Lines creating assets and verifying JSON structures look repetitive. You might consider factoring out a helper function that wraps creation of the asset plus validation of its fields to avoid duplication and reduce potential for inconsistencies.
216-243
: Synchronized logic for file data creation.
createAssetFromFileData
mirrorscreateAsset
. Consider deduplicating these if possible—using a shared helper for request building and file handling avoids divergences.server/internal/adapter/gql/gqlmodel/convert_asset.go (1)
12-16
: Consider a more concise implementation.While the current implementation is correct, it could be simplified for better readability.
- var pid *ID - if project := a.Project(); project != nil { - pidValue := IDFrom(*a.Project()) - pid = &pidValue - } + var pid *ID + if p := a.Project(); p != nil { + id := IDFrom(*p) + pid = &id + }Also applies to: 22-22
server/internal/adapter/gql/resolver_mutation_asset.go (1)
19-26
: Improve error variable naming for consistency.The implementation is correct, but consider improving error variable naming for consistency with the rest of the file.
- pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID) - if err != nil { + pidValue, err2 := gqlmodel.ToID[id.Project](*input.ProjectID) + if err2 != nil {Note: This matches the error variable naming pattern used in the RemoveAsset function (line 48).
Also applies to: 30-30
server/internal/infrastructure/memory/asset.go (1)
49-59
: Consider extracting the filter condition for better maintainability.The implementation is correct, but the filter conditions could be extracted into a separate function to improve readability and maintainability.
func (r *Asset) FindByWorkspace(_ context.Context, wid accountdomain.WorkspaceID, pid *id.ProjectID, filter repo.AssetFilter) ([]*asset.Asset, *usecasex.PageInfo, error) { + filterAsset := func(v *asset.Asset) bool { + baseCondition := v.CoreSupport() && (filter.Keyword == nil || strings.Contains(v.Name(), *filter.Keyword)) + if pid != nil { + return v.Project() != nil && *v.Project() == *pid && baseCondition + } + return v.Workspace() == wid && baseCondition + } + result := r.data.FindAll(func(k id.AssetID, v *asset.Asset) bool { - if pid != nil { - return v.Project() != nil && *v.Project() == *pid && v.CoreSupport() && (filter.Keyword == nil || strings.Contains(v.Name(), *filter.Keyword)) - } - return v.Workspace() == wid && v.CoreSupport() && (filter.Keyword == nil || strings.Contains(v.Name(), *filter.Keyword)) + return filterAsset(v) })web/src/services/api/assetsApi.ts (1)
116-116
: Consider implementing error tracking.The TODO comment indicates missing error tracking implementation.
Would you like me to help implement error tracking using a standard error tracking library?
server/e2e/common.go (2)
288-293
: Consider handling additional JSON value types.The function only handles
map[string]interface{}
but could be enhanced to support arrays and primitive types.Apply this diff to improve type handling:
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { - if actualData, ok := actual.Raw().(map[string]interface{}); ok { + switch actualData := actual.Raw().(type) { + case map[string]interface{}, []interface{}, string, float64, bool: return JSONEqRegexpInterface(t, actualData, expected) + default: + return false } - return false }
305-319
: Enhance error handling in ValueDump function.Consider logging errors when JSON marshaling fails and handling nil values.
Apply this diff to improve error handling:
func ValueDump(val *httpexpect.Value) { + if val == nil { + fmt.Println("Value is nil") + return + } raw := val.Raw() switch data := raw.(type) { case map[string]interface{}: if text, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Println(string(text)) + } else { + fmt.Printf("Error marshaling map: %v\n", err) } case []interface{}: if text, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Println(string(text)) + } else { + fmt.Printf("Error marshaling array: %v\n", err) } default: fmt.Println("Unsupported type:", reflect.TypeOf(raw)) } }server/internal/adapter/gql/resolver_mutation_project.go (2)
262-272
: Improve error message clarity.The error messages could be more descriptive to help with debugging.
Apply this diff to improve error messages:
projectData, _ := unmarshalProject(tempData) prj, tx, err := usecases(ctx).Project.ImportProject(ctx, workspace.String(), projectData) if err != nil { - return nil, errors.New("Fail ImportProject :" + err.Error()) + return nil, fmt.Errorf("failed to import project (workspace: %s): %w", workspace.String(), err) } defer func() { if err2 := tx.End(ctx); err == nil && err2 != nil { err = err2 } }()
287-292
: Improve error handling for asset upload.The error handling could be more descriptive and consistent with other error messages.
Apply this diff to improve error handling:
pid, err := id.ProjectIDFrom(prj.ID().String()) if err != nil { - return nil, errors.New("Fail UploadAssetFile :" + err.Error()) + return nil, fmt.Errorf("failed to parse project ID %s: %w", prj.ID().String(), err) } url, _, err := usecases(ctx).Asset.UploadAssetFile(ctx, realName, file, workspace, &pid)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (3)
web/src/services/gql/__gen__/fragmentMatcher.json
is excluded by!**/__gen__/**
web/src/services/gql/__gen__/gql.ts
is excluded by!**/__gen__/**
web/src/services/gql/__gen__/graphql.ts
is excluded by!**/__gen__/**
📒 Files selected for processing (82)
server/Makefile
(3 hunks)server/e2e/common.go
(3 hunks)server/e2e/dataset_export_test.go
(0 hunks)server/e2e/gql_asset_test.go
(7 hunks)server/e2e/gql_custom_property_test.go
(1 hunks)server/e2e/gql_layer_test.go
(0 hunks)server/e2e/gql_nlslayer_test.go
(0 hunks)server/e2e/gql_project_import_test.go
(1 hunks)server/e2e/gql_storytelling_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/seeder.go
(1 hunks)server/gql/asset.graphql
(3 hunks)server/gql/cluster.graphql
(0 hunks)server/gql/dataset.graphql
(0 hunks)server/gql/layer.graphql
(0 hunks)server/gql/plugin.graphql
(2 hunks)server/gql/property.graphql
(0 hunks)server/gql/scene.graphql
(1 hunks)server/gql/tag.graphql
(0 hunks)server/gql/workspace.graphql
(2 hunks)server/gqlgen.yml
(0 hunks)server/internal/adapter/gql/context.go
(0 hunks)server/internal/adapter/gql/gqldataloader/dataloader.go
(0 hunks)server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
(0 hunks)server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
(0 hunks)server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
(0 hunks)server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
(0 hunks)server/internal/adapter/gql/gqldataloader/tagloader_gen.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_asset.go
(1 hunks)server/internal/adapter/gql/gqlmodel/convert_dataset.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_layer.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_plugin.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_scene.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_tag.go
(0 hunks)server/internal/adapter/gql/gqlmodel/models.go
(0 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(6 hunks)server/internal/adapter/gql/loader.go
(0 hunks)server/internal/adapter/gql/loader_asset.go
(2 hunks)server/internal/adapter/gql/loader_dataset.go
(0 hunks)server/internal/adapter/gql/loader_tag.go
(0 hunks)server/internal/adapter/gql/resolver_dataset.go
(0 hunks)server/internal/adapter/gql/resolver_dataset_schema.go
(0 hunks)server/internal/adapter/gql/resolver_layer.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_asset.go
(2 hunks)server/internal/adapter/gql/resolver_mutation_dataset.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_layer.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_project.go
(3 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_tag.go
(0 hunks)server/internal/adapter/gql/resolver_property.go
(0 hunks)server/internal/adapter/gql/resolver_property_test.go
(0 hunks)server/internal/adapter/gql/resolver_query.go
(1 hunks)server/internal/adapter/gql/resolver_scene.go
(0 hunks)server/internal/adapter/gql/resolver_tag.go
(0 hunks)server/internal/adapter/gql/resolver_team.go
(1 hunks)server/internal/infrastructure/memory/asset.go
(1 hunks)server/internal/infrastructure/mongo/asset.go
(2 hunks)server/internal/infrastructure/mongo/mongodoc/asset.go
(3 hunks)server/internal/usecase/interactor/asset.go
(4 hunks)server/internal/usecase/interactor/layer.go
(0 hunks)server/internal/usecase/interactor/scene.go
(0 hunks)server/internal/usecase/interactor/scene_test.go
(0 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/repo/asset.go
(1 hunks)server/pkg/asset/asset.go
(2 hunks)server/pkg/asset/builder.go
(1 hunks)server/pkg/asset/id.go
(1 hunks)server/pkg/layer/encoding/exporter.go
(1 hunks)server/pkg/layer/layerops/processor_test.go
(0 hunks)server/pkg/scene/builder/builder.go
(1 hunks)server/pkg/scene/builder/builder_test.go
(0 hunks)server/pkg/scene/builder/encoder.go
(1 hunks)server/pkg/scene/builder/encoder_test.go
(0 hunks)server/pkg/scene/builder/scene.go
(1 hunks)web/src/beta/features/AssetsManager/hooks.ts
(2 hunks)web/src/beta/features/AssetsManager/index.tsx
(3 hunks)web/src/beta/features/Dashboard/ContentsContainer/Assets/index.tsx
(2 hunks)web/src/services/api/assetsApi.ts
(2 hunks)web/src/services/api/propertyApi/utils.ts
(1 hunks)web/src/services/api/sceneApi.ts
(0 hunks)web/src/services/gql/fragments/dataset.ts
(0 hunks)
⛔ Files not processed due to max files limit (5)
- web/src/services/gql/fragments/index.ts
- web/src/services/gql/fragments/layer.ts
- web/src/services/gql/fragments/property.ts
- web/src/services/gql/queries/asset.ts
- web/src/services/gql/queries/scene.ts
💤 Files with no reviewable changes (45)
- server/internal/adapter/gql/context.go
- server/internal/adapter/gql/gqlmodel/convert_scene.go
- server/internal/adapter/gql/gqldataloader/dataloader.go
- web/src/services/gql/fragments/dataset.ts
- server/internal/adapter/gql/gqlmodel/convert.go
- server/internal/usecase/interactor/scene_test.go
- server/pkg/scene/builder/builder_test.go
- web/src/services/api/sceneApi.ts
- server/pkg/layer/layerops/processor_test.go
- server/internal/adapter/gql/gqlmodel/convert_plugin.go
- server/gql/property.graphql
- server/internal/adapter/gql/resolver_property_test.go
- server/pkg/scene/builder/encoder_test.go
- server/internal/usecase/interactor/scene.go
- server/gqlgen.yml
- server/internal/adapter/gql/gqlmodel/convert_layer.go
- server/gql/cluster.graphql
- server/internal/adapter/gql/resolver_scene.go
- server/e2e/gql_layer_test.go
- server/internal/adapter/gql/resolver_layer.go
- server/internal/adapter/gql/gqlmodel/convert_tag.go
- server/e2e/gql_nlslayer_test.go
- server/e2e/dataset_export_test.go
- server/internal/usecase/interactor/layer.go
- server/gql/dataset.graphql
- server/internal/adapter/gql/gqlmodel/convert_dataset.go
- server/internal/adapter/gql/gqlmodel/models.go
- server/internal/adapter/gql/resolver_mutation_scene.go
- server/internal/adapter/gql/resolver_mutation_tag.go
- server/internal/adapter/gql/loader.go
- server/internal/adapter/gql/resolver_mutation_layer.go
- server/internal/adapter/gql/resolver_dataset.go
- server/internal/adapter/gql/resolver_mutation_dataset.go
- server/internal/adapter/gql/resolver_dataset_schema.go
- server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
- server/internal/adapter/gql/gqldataloader/tagloader_gen.go
- server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
- server/internal/adapter/gql/resolver_tag.go
- server/gql/tag.graphql
- server/internal/adapter/gql/resolver_property.go
- server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
- server/gql/layer.graphql
- server/internal/adapter/gql/loader_tag.go
- server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
- server/internal/adapter/gql/loader_dataset.go
✅ Files skipped from review due to trivial changes (1)
- server/pkg/scene/builder/encoder.go
🧰 Additional context used
📓 Learnings (1)
server/internal/adapter/gql/resolver_mutation_project.go (1)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:215-217
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project import functionality, the file names are unique and can be safely replaced uniformly using `bytes.Replace`.
🪛 GitHub Actions: ci
web/src/beta/features/AssetsManager/hooks.ts
[error] 212-212: React Hook useCallback has a missing dependency: 'projectId'. Either include it or remove the dependency array.
server/e2e/seeder.go
[warning] 28-28: var sID
is unused.
🪛 golangci-lint (1.62.2)
server/e2e/seeder.go
28-28: var sID
is unused
(unused)
🪛 GitHub Check: ci-server / ci-server-lint
server/e2e/seeder.go
[failure] 28-28:
var sID
is unused (unused)
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Redirect rules - reearth-web
- GitHub Check: Header rules - reearth-web
- GitHub Check: Pages changed - reearth-web
🔇 Additional comments (55)
server/pkg/scene/builder/scene.go (2)
11-25
: LGTM! Clean removal of deprecated fields.The removal of Layers, Tags, and Clusters fields from the sceneJSON struct aligns with the broader refactoring effort to simplify the data model.
27-40
: LGTM! Method signature updated correctly.The sceneJSON method has been updated to remove the layers parameter while maintaining all essential functionality.
server/e2e/gql_project_import_test.go (1)
80-82
: LGTM! Clean formatting adjustment.The formatting change improves code readability without affecting functionality.
server/gql/scene.graphql (2)
1-17
: LGTM! Scene type simplified effectively.The removal of deprecated fields (rootLayerId, rootLayer, datasetSchemas, tagIds, tags, clusters) streamlines the Scene type while maintaining all essential functionality.
50-52
: LGTM! Query formatting improved.The formatting change in the Query type extension improves readability.
server/gql/plugin.graphql (2)
20-30
: LGTM! Removed deprecated extension type.The removal of the Cluster extension type is consistent with the broader refactoring effort.
99-109
: LGTM! Improved formatting.The formatting changes in the Query and Mutation type extensions improve code readability.
server/e2e/seeder.go (1)
1-91
: Verify alignment with PR objectives.The changes in this file primarily involve removing dataset-related code, but the PR objectives focus on adding project asset association functionality. Please verify if additional test setup code is needed to cover the new project asset association feature.
Would you like me to help generate test setup code for the new project asset association feature?
🧰 Tools
🪛 golangci-lint (1.62.2)
28-28: var
sID
is unused(unused)
🪛 GitHub Check: ci-server / ci-server-lint
[failure] 28-28:
varsID
is unused (unused)🪛 GitHub Actions: ci
[warning] 28-28: var
sID
is unused.server/internal/adapter/gql/gqlmodel/models_gen.go (5)
138-138
: Ensure optional field is properly handled.
The addition ofProjectID
as an optional field aligns with the PR objectives for associating assets with projects. Please verify that usage in resolvers or services properly accounts for the possibility of a nil project reference.
186-186
: Confirm validation forprojectId
in creation flow.
AddingprojectId
toCreateAssetInput
is consistent with the new association feature. Ensure relevant logic (e.g., create mutation) properly validates whenprojectId
is omitted or points to a non-existent project.
581-588
: Struct changes look good.
These newly introduced fields inMergedPropertyField
seem consistent with the merged property concept. No issues detected.
919-921
: New fields appear consistent.
These new fields inPropertyFieldLink
correctly reflect optional vs. required attributes.
1834-1834
: Enum coverage update looks valid.
IncludingPluginExtensionTypeInfoboxBlock
in theIsValid
switch branch is appropriate.server/pkg/scene/builder/builder.go (1)
176-181
: Verify layer export functionality after encoder removal.The removal of the encoder and direct usage of the exporter changes how layers are processed. While the code looks correct, we should verify that:
- All layer data is properly exported via
ExportLayerByID
- The scene JSON generation receives all necessary layer information through the context
✅ Verification successful
🏁 Script executed:
#!/bin/bash # Search for any direct encoder usage that might have been missed rg -A 5 "encoder\." server/pkg/scene/ # Check for any layer-related tests that might need updates fd -e go -e test.go -x rg -l "TestBuild.*Scene|TestExport.*Layer" {} \;Length of output: 154
🏁 Script executed:
#!/bin/bash # Check the definition of ExportLayerByID in the repository to inspect how it handles layer data. rg -A 20 "func.*ExportLayerByID" -n . # Check the implementation of sceneJSON within the Builder to verify that it processes the exported layer data correctly. rg -A 20 "func (b *Builder) sceneJSON" -n server/pkg/scene/builder/builder.goLength of output: 1450
Layer export functionality verified post-encoder removal.
The exported layer data is now processed via
b.exporter.ExportLayerByID
, which internally merges and encodes the layer correctly. No residual usage of the encoder was found, and the existing tests (e.g., inserver/pkg/property/builder_test.go
) confirm that scene construction viasceneJSON
receives the necessary layer data.server/e2e/gql_asset_test.go (7)
16-18
: Rename is appropriate but consider clarifying doc comments.Renaming the test function to
TestCoreSupportGetAssets
aligns better with its purpose of testing core support for created assets. Consider adding a short comment describing the test's high-level objective for future maintainers.
113-113
: Enforce domain constraints on workspace searching.When calling
FindByWorkspace()
with anil
project filter, ensure the rest of the domain logic still enforces correctness (like workspace membership, valid statuses, etc.).
121-122
: Verify consistency of coreSupport in results.The test strictly checks
coreSupport == true
for every returned asset. If some assets are intended to havefalse
, confirm this query or filter is correct; otherwise, the test might mask unexpected data.
129-171
: Test covers multiple project associations thoroughly.The
TestAssociateProjectGetAssets
function offers good coverage for assets without a project and with different projects. Consider adding error-path checks (e.g., invalid project IDs) to bolster reliability.
173-190
: GraphQL mutation structure looks correct.Adding
projectId
toCreateAssetInput
is consistent with the PR goal. Confirm all references to this mutation in the codebase or frontend are updated to avoid signature mismatches.
192-214
: OptionalprojectId
parameter is handled properly.Allowing
nil
forprojectId
is logical for workspace-level assets. Validating it in the create function can prevent invalid references (e.g., unknown project).
245-308
: Query is comprehensive but watch performance.The
getAssets
query includes pagination, filtering, and many fields. While thorough, ensure performance is acceptable under large data loads; you might consider smaller queries or only requesting necessary fields in certain contexts.server/e2e/gql_custom_property_test.go (1)
347-762
: Extensive multi-fragment GraphQL query is well-structured.Using fragments for
PropertyFragment
,WidgetAlignSystemFragment
, etc., helps keep the query organized. However, it's quite large. If performance or clarity issues arise, consider splitting queries by domain area or introducing smaller resolvers.server/e2e/gql_storytelling_test.go (2)
735-741
: Refactored configuration for test initialization.Separating configuration into a variable
c
before callingStartServerAndRepos
improves readability and maintainability. Ensure any additional test config toggles remain consistent across all tests.
770-840
: Confirm published JSON matches updated schema.The large JSON structure checks many fields, including new properties like
coreSupport
. If your schema evolves, remember to adjust these expected values to prevent brittle tests. Also, consider verifying the final published file includes all required fields (e.g.,property.tiles
).server/internal/usecase/repo/asset.go (1)
20-20
: LGTM! The interface change aligns with PR objectives.The updated
FindByWorkspace
method signature correctly adds project filtering capability while maintaining backward compatibility with an optional project ID parameter.server/pkg/layer/encoding/exporter.go (2)
37-42
: Verify the intentional removal of encoding functionality.The
Encode
method now returnsnil
without performing any operations. This could lead to silent failures as callers expect encoding to happen.Please confirm:
- Is this change intentional?
- Should we add a warning log or error return to indicate that encoding is not implemented?
- Should we update the method documentation to reflect this change?
10-13
: Consider documenting the removal of encoding functionality.The
Encoder
field has been removed from theExporter
struct, significantly changing its behavior.Let's verify if any callers of this struct need to be updated:
✅ Verification successful
🏁 Script executed:
#!/bin/bash # Search for usages of Exporter struct and Encode method rg -A 5 "Exporter.*{|Encode\(" --type goLength of output: 156
🏁 Script executed:
#!/bin/bash # Search for the Exporter struct definition to confirm its current fields rg -A 5 "type Exporter struct" --type go # Search for any calls to an Encode method (which might be referencing the removed Encoder functionality) rg -A 5 "\.Encode\(" --type go # Additionally, search for any references to the removed 'Encoder' field in usages of Exporter rg -A 5 "Exporter.*Encoder" --type goLength of output: 5000
No caller updates are necessary since the new
Encode
method is properly used and there are no references to the previously removedEncoder
field.
- The grep output confirms that the
Exporter
struct now only contains theMerger
andSealer
fields.- All invocations of encoding functionality directly call the
Encode
method on theExporter
instance.- There are no lingering references to an
Encoder
field anywhere in the codebase.It would still be beneficial to add a documentation note describing the removal of the
Encoder
field and how encoding is now handled.server/pkg/asset/id.go (1)
10-10
: LGTM! Well-structured ID type definitions.The new ProjectID type and associated helper functions follow consistent naming patterns and maintain code organization.
Also applies to: 14-14, 18-18, 22-22, 26-26
web/src/beta/features/Dashboard/ContentsContainer/Assets/index.tsx (1)
7-10
: LGTM! Clean implementation of project association in the UI.The changes correctly implement project filtering while maintaining backward compatibility with the optional
projectId
prop.Also applies to: 12-12, 29-31
server/pkg/asset/asset.go (1)
18-18
: LGTM! Clean implementation of project association.The optional project field and its getter method are well-implemented, following the established patterns in the codebase.
Also applies to: 34-36
server/internal/usecase/interfaces/asset.go (1)
28-28
: Verify implementations of the updated interface methods.The interface changes look good, but please ensure all implementations of the Asset interface have been updated to match these changes:
- FindByWorkspace with new ProjectID parameter
- UploadAssetFile with new ProjectID parameter
Also applies to: 39-39, 42-42
server/internal/adapter/gql/resolver_team.go (1)
27-34
: LGTM! The changes correctly implement project filtering for team assets.The updated
Assets
method signature and implementation properly handle the optional project ID parameter, aligning with the PR objectives.server/pkg/asset/builder.go (1)
57-60
: LGTM! The Project method follows the builder pattern consistently.The implementation maintains the fluent interface pattern used throughout the builder, correctly handling the optional project ID.
server/internal/infrastructure/mongo/mongodoc/asset.go (3)
17-17
: LGTM! Project field added to AssetDocument.The field is correctly defined as an optional string pointer, consistent with MongoDB document patterns.
35-40
: LGTM! Project ID conversion is properly handled in NewAsset.The implementation correctly handles the optional project ID conversion to string format for MongoDB storage.
65-72
: LGTM! Model method properly handles project ID conversion.The implementation includes proper error handling for project ID conversion from MongoDB format.
server/internal/adapter/gql/loader_asset.go (2)
39-54
: LGTM! Project ID handling is properly implemented.The implementation includes proper error handling for project ID conversion and maintains consistency with other ID handling patterns.
60-66
: Nice optimization of edge creation!Pre-allocating the edges slice and using an indexed loop improves performance by avoiding slice growth operations.
server/e2e/gql_validate_geojson_test.go (1)
186-186
: LGTM! Test updated correctly for new API signature.The test has been properly updated to include the new
projectId
parameter as nil, maintaining backward compatibility.web/src/services/api/assetsApi.ts (1)
74-76
: LGTM! Correctly added projectId to asset creation.The projectId parameter has been properly integrated into the asset creation flow.
server/internal/usecase/interactor/asset.go (1)
39-51
: Consider adding project ID validation.The FindByWorkspace implementation should validate that the project belongs to the workspace when a project ID is provided.
server/internal/infrastructure/mongo/asset.go (2)
67-67
: LGTM! Method signature updated to support project-based filtering.The addition of
projectId
parameter aligns with the PR objective of associating assets with projects.
72-80
: LGTM! Filter construction handles both project and workspace scenarios.The implementation correctly:
- Uses project ID for filtering when provided
- Falls back to workspace ID when project ID is not provided
server/internal/adapter/gql/resolver_query.go (1)
15-16
: LGTM! GraphQL resolver updated to support project-based asset filtering.The changes correctly propagate the
projectId
parameter to the asset loader.web/src/beta/features/AssetsManager/index.tsx (2)
24-24
: LGTM! Type definition updated to support project association.The optional
projectId
prop allows the component to be used in both project-specific and workspace-wide contexts.
37-37
: LGTM! Component correctly handles the projectId prop.The implementation properly:
- Destructures
projectId
from props- Passes it to
useHooks
for handling asset operationsAlso applies to: 69-69
web/src/beta/features/AssetsManager/hooks.ts (1)
30-31
: LGTM!The optional
projectId
parameter has been correctly added to both the function signature and type definition.Also applies to: 38-39
server/gql/asset.graphql (3)
5-5
: LGTM!The optional
projectId
field has been correctly added to the Asset type.
24-24
: LGTM!The optional
projectId
field has been correctly added to the CreateAssetInput type.
65-65
: LGTM!The optional
projectId
parameter has been correctly added to the assets query.server/gql/workspace.graphql (3)
8-14
: Enhance Asset Query with Optional 'projectId' Parameter
The assets field now accepts an optionalprojectId
parameter, which will enable filtering assets by their associated project. Please ensure that the corresponding resolver logic is updated to handle this parameter.
15-21
: Consistent Formatting for Projects Field
The parameters for the projects field have been reformatted for improved readability. There are no functional changes, so this update is acceptable.
121-123
: Refactored Mutation Field for removeMemberFromTeam
The formatting for theremoveMemberFromTeam
mutation has been updated to a multi-line structure. This change improves readability while preserving functionality.server/Makefile (2)
85-90
: New 'run-reset' Target Added
Therun-reset
target has been introduced to reset the MongoDB environment by stopping the container (reearth-visualizer-reearth-mongo-1
), removing old data directories, and restarting the database along with initializing a mock user. Please verify that the Docker container name is consistent with your deployment configuration. As a future improvement, consider adding error handling (e.g., checking if the container is running before attempting to stop it) to make the process more robust.
112-112
: Updated .PHONY Declaration
Includingrun-reset
in the .PHONY declaration ensures that the target is always executed correctly. This update is correct and helps prevent potential issues with file conflicts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (4)
web/src/services/gql/queries/asset.ts (1)
58-66
: Consider adding error handling to the UPDATE_ASSET mutation.The new mutation looks good, but it would be beneficial to include error information in the response.
export const UPDATE_ASSET = gql(` mutation UpdateAsset($assetId: ID!, $projectId: ID) { updateAsset(input: { assetId: $assetId projectId: $projectId }) { assetId projectId + errors { + message + path + } __typename } } `);server/e2e/common.go (2)
288-293
: Enhance error handling in JSONEqRegexpValue.The function could be more robust by handling additional types and providing better error feedback.
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { + if actual == nil { + t.Error("actual value is nil") + return false + } if actualData, ok := actual.Raw().(map[string]interface{}); ok { return JSONEqRegexpInterface(t, actualData, expected) } + t.Errorf("expected map[string]interface{}, got %T", actual.Raw()) return false }
305-319
: Improve error handling in ValueDump.The function silently ignores marshaling errors and could provide better feedback.
func ValueDump(val *httpexpect.Value) { + if val == nil { + fmt.Println("Error: nil value") + return + } raw := val.Raw() switch data := raw.(type) { case map[string]interface{}: if text, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Println(string(text)) + } else { + fmt.Printf("Error marshaling map: %v\n", err) } case []interface{}: if text, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Println(string(text)) + } else { + fmt.Printf("Error marshaling array: %v\n", err) } default: fmt.Println("Unsupported type:", reflect.TypeOf(raw)) } }server/e2e/gql_asset_test.go (1)
268-271
: Remove commented code.The commented code block is unnecessary and should be removed for better code cleanliness.
- // "input": map[string]interface{}{ - // "assetId": assetId, - // },
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
web/src/services/gql/__gen__/gql.ts
is excluded by!**/__gen__/**
web/src/services/gql/__gen__/graphql.ts
is excluded by!**/__gen__/**
📒 Files selected for processing (11)
server/e2e/common.go
(4 hunks)server/e2e/gql_asset_test.go
(7 hunks)server/gql/asset.graphql
(5 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(7 hunks)server/internal/adapter/gql/resolver_mutation_asset.go
(3 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/pkg/asset/asset.go
(2 hunks)web/src/beta/features/AssetsManager/hooks.ts
(2 hunks)web/src/beta/ui/fields/AssetField/useAssetUpload.ts
(1 hunks)web/src/services/gql/queries/asset.ts
(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- web/src/beta/features/AssetsManager/hooks.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Redirect rules - reearth-web
- GitHub Check: Header rules - reearth-web
- GitHub Check: Pages changed - reearth-web
🔇 Additional comments (14)
server/internal/adapter/gql/gqlmodel/models_gen.go (5)
906-915
: Use a consistent type forID
inPropertyField
.The
ID
field in thePropertyField
struct is declared asstring
, while other ID fields use the customID
type. For consistency and type safety, consider changing it to use theID
type.
138-138
: Addition ofProjectID
toAsset
struct.The optional
ProjectID
field has been added to theAsset
struct to associate assets with projects. Ensure that this addition aligns with the overall data model and that any serialization or validation logic accounts for this new field.
186-186
: UpdateCreateAssetInput
with optionalprojectId
.The
CreateAssetInput
struct now includes an optionalprojectId
field. This allows for the creation of assets associated with a specific project. Confirm that all GraphQL mutations using this input handle the new field correctly and that appropriate validations are in place.
1330-1333
: IntroduceUpdateAssetInput
with optionalprojectId
.A new
UpdateAssetInput
type with an optionalprojectId
field has been added. This enables updating an asset's associated project. Verify that the update operations correctly handle changes to theprojectId
and maintain data integrity.
581-589
: Verify the correctness of added fields inMergedPropertyField
.New fields have been added to the
MergedPropertyField
struct, includingSchemaID
,FieldID
,Value
,Type
,Links
,Overridden
,Schema
, andField
. Ensure that these fields are properly initialized and integrated within the system's property merging logic.server/pkg/asset/asset.go (2)
18-18
: Addition of optionalproject
field toAsset
struct.The
Asset
struct now includes an optionalproject
field of type*ProjectID
, allowing assets to be associated with a project. Verify that this new field is correctly handled throughout the codebase, especially during asset creation and serialization.
34-40
: ImplementProject()
andSetProject()
methods inAsset
.The new methods
Project()
andSetProject()
provide access to theproject
field. Ensure that these methods are used appropriately and that they do not introduce unintended side effects. Additionally, confirm that any concurrency considerations are addressed if the asset may be accessed by multiple goroutines.server/internal/usecase/interfaces/asset.go (2)
28-28
: IncludeProjectID
inCreateAssetParam
.The
CreateAssetParam
struct now includes aProjectID
field. Ensure that this parameter is correctly utilized in asset creation workflows and that necessary validations are in place when associating an asset with a project.
41-41
: AddUpdate
method toAsset
interface.A new
Update
method has been added to theAsset
interface to allow updating an asset'sProjectID
. Ensure that this method is implemented consistently across all implementations of the interface and that it properly handles concurrent access and error conditions.web/src/services/gql/queries/asset.ts (1)
4-5
: LGTM! The query parameters have been updated correctly.The
GET_ASSETS
query has been enhanced with an optionalprojectId
parameter, allowing for project-specific asset filtering.server/internal/adapter/gql/resolver_mutation_asset.go (1)
19-26
: Add validation for project-workspace relationship.The CreateAsset resolver should verify that the provided project belongs to the workspace before creating the asset.
server/internal/usecase/interactor/asset.go (1)
108-127
: Consider adding transaction rollback on error.The Update method uses a transaction but doesn't explicitly handle rollback on error. While the transaction wrapper might handle this, it would be good to verify the behavior.
✅ Verification successful
🏁 Script executed:
#!/bin/bash # Check how transactions are handled in the codebase rg "Transaction\(\)" -A 10 --type goLength of output: 7950
Transaction Rollback Is Handled by the Transaction Wrapper
The transaction infrastructure (using
Usecase().Transaction()
in combination withRun2
) automatically rolls back the transaction upon error. This behavior is confirmed by tests inserver/internal/usecase/interactor/usecase_test.go
where error cases assert that the transaction is not committed.server/e2e/gql_asset_test.go (1)
131-195
: LGTM! Comprehensive test coverage for project association.The test cases thoroughly cover:
- Asset creation with project association
- Asset retrieval by project
- Asset movement between projects
- Asset removal from projects
- Asset deletion
server/gql/asset.graphql (1)
1-87
: LGTM! Well-structured GraphQL schema changes.The changes consistently implement project association across all relevant types and operations:
- Asset type includes optional projectId
- Input types properly handle projectId
- Query and mutation operations support project filtering
d6b5ab7
to
ae2ce12
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
server/internal/adapter/gql/gqlmodel/models_gen.go (1)
906-915
: 🛠️ Refactor suggestionUse consistent type for
ID
inPropertyField
The
ID
field in thePropertyField
struct is declared asstring
, whereas other ID fields use the customID
type. For consistency and improved type safety, consider changing theID
field to use theID
type.Apply this diff to align the
ID
type:- ID string `json:"id"` + ID ID `json:"id"`
🧹 Nitpick comments (6)
server/internal/adapter/gql/resolver_team.go (1)
27-34
: Consider documenting pagination parameters.The implementation correctly handles the new projectID parameter, but the pagination parameters (first, last, after, before) could benefit from documentation explaining their usage and limitations.
server/internal/infrastructure/mongo/asset.go (1)
67-103
: Improve filter construction for better maintainability.The current implementation has separate branches for project and team filtering. Consider using a more maintainable approach by building the filter dynamically.
func (r *Asset) FindByWorkspace(ctx context.Context, id accountdomain.WorkspaceID, projectId *id.ProjectID, uFilter repo.AssetFilter) ([]*asset.Asset, *usecasex.PageInfo, error) { if !r.f.CanRead(id) { return nil, usecasex.EmptyPageInfo(), nil } - filter := bson.M{ - "coresupport": true, - } - - if projectId != nil { - filter["project"] = projectId.String() - } else { - filter["team"] = id.String() - } + filter := bson.M{"coresupport": true} + + // Build filter based on project or team + if projectId != nil { + filter["project"] = projectId.String() + filter["team"] = id.String() // Always include team check for security + } else { + filter["team"] = id.String() + }server/e2e/common.go (2)
288-294
: Enhance JSON comparison to handle more types.The function only handles map[string]interface{} but should support arrays and other JSON types for better test coverage.
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { - if actualData, ok := actual.Raw().(map[string]interface{}); ok { - return JSONEqRegexpInterface(t, actualData, expected) + switch actualData := actual.Raw().(type) { + case map[string]interface{}, []interface{}: + return JSONEqRegexpInterface(t, actualData, expected) + default: + t.Errorf("Unsupported type for JSON comparison: %T", actualData) + return false } - return false }
305-319
: Reduce code duplication in JSON marshaling.The JSON marshaling logic is duplicated for map and slice types. Consider extracting it to a helper function.
+func marshalAndPrint(data interface{}) { + if text, err := json.MarshalIndent(data, "", " "); err == nil { + fmt.Println(string(text)) + } +} + func ValueDump(val *httpexpect.Value) { raw := val.Raw() switch data := raw.(type) { case map[string]interface{}: - if text, err := json.MarshalIndent(data, "", " "); err == nil { - fmt.Println(string(text)) - } + marshalAndPrint(data) case []interface{}: - if text, err := json.MarshalIndent(data, "", " "); err == nil { - fmt.Println(string(text)) - } + marshalAndPrint(data) default: fmt.Println("Unsupported type:", reflect.TypeOf(raw)) } }server/internal/adapter/gql/resolver_mutation_project.go (1)
287-292
: Consider enhancing error message.The error message could be more descriptive to help with debugging.
- return nil, errors.New("Fail UploadAssetFile :" + err.Error()) + return nil, errors.New("Failed to parse project ID during asset upload: " + err.Error())server/internal/adapter/gql/gqlmodel/models_gen.go (1)
1844-1845
: Ensure consistent string values inPluginExtensionType
constantsThe new
PluginExtensionType
constants have mixed-case string values (e.g.,"Story"
,"StoryPage"
), while existing constants use uppercase (e.g.,"PRIMITIVE"
,"WIDGET"
). For consistency and to prevent potential issues during serialization, consider updating the string values of the new constants to uppercase.Apply this diff to standardize the string values:
- PluginExtensionTypeStory PluginExtensionType = "Story" - PluginExtensionTypeStoryPage PluginExtensionType = "StoryPage" - PluginExtensionTypeStoryBlock PluginExtensionType = "StoryBlock" - PluginExtensionTypeInfoboxBlock PluginExtensionType = "InfoboxBlock" + PluginExtensionTypeStory PluginExtensionType = "STORY" + PluginExtensionTypeStoryPage PluginExtensionType = "STORYPAGE" + PluginExtensionTypeStoryBlock PluginExtensionType = "STORYBLOCK" + PluginExtensionTypeInfoboxBlock PluginExtensionType = "INFOBOXBLOCK"Also, update the
IsValid
method and any references to these constants accordingly.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
server/e2e/common.go
(4 hunks)server/e2e/gql_asset_test.go
(7 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/seeder.go
(0 hunks)server/gql/asset.graphql
(5 hunks)server/gql/workspace.graphql
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_asset.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(7 hunks)server/internal/adapter/gql/loader_asset.go
(2 hunks)server/internal/adapter/gql/resolver_mutation_asset.go
(3 hunks)server/internal/adapter/gql/resolver_mutation_project.go
(3 hunks)server/internal/adapter/gql/resolver_query.go
(1 hunks)server/internal/adapter/gql/resolver_team.go
(1 hunks)server/internal/infrastructure/memory/asset.go
(1 hunks)server/internal/infrastructure/mongo/asset.go
(2 hunks)server/internal/infrastructure/mongo/mongodoc/asset.go
(3 hunks)server/internal/usecase/interactor/asset.go
(5 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/repo/asset.go
(1 hunks)server/pkg/asset/asset.go
(2 hunks)server/pkg/asset/builder.go
(1 hunks)server/pkg/asset/id.go
(1 hunks)server/pkg/layer/layerops/processor_test.go
(0 hunks)
💤 Files with no reviewable changes (2)
- server/pkg/layer/layerops/processor_test.go
- server/e2e/seeder.go
🚧 Files skipped from review as they are similar to previous changes (9)
- server/internal/adapter/gql/gqlmodel/convert_asset.go
- server/pkg/asset/builder.go
- server/internal/usecase/repo/asset.go
- server/internal/infrastructure/memory/asset.go
- server/pkg/asset/asset.go
- server/gql/asset.graphql
- server/internal/infrastructure/mongo/mongodoc/asset.go
- server/e2e/gql_validate_geojson_test.go
- server/internal/adapter/gql/loader_asset.go
🧰 Additional context used
📓 Learnings (1)
server/internal/adapter/gql/resolver_mutation_project.go (1)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:215-217
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project import functionality, the file names are unique and can be safely replaced uniformly using `bytes.Replace`.
🔇 Additional comments (19)
server/pkg/asset/id.go (1)
10-10
: LGTM! Well-structured ID type implementation.The new ProjectID type and its associated functions follow the established patterns consistently.
Also applies to: 14-14, 18-18, 22-22, 26-26
server/internal/usecase/interfaces/asset.go (2)
28-29
: LGTM! ProjectID field added to CreateAssetParam.The addition follows the struct's existing pattern.
39-39
: Verify interface implementations for updated method signatures.The Asset interface has been updated with ProjectID parameters. Ensure all implementations (mongo, memory, etc.) are updated accordingly.
Also applies to: 41-41, 43-43
✅ Verification successful
🏁 Script executed:
#!/bin/bash # Search for Asset interface implementations ast-grep --pattern 'type $_ struct { $$$ } func (r $_) FindByWorkspace(ctx context.Context, wid accountdomain.WorkspaceID, pid *id.ProjectID, $$$) ($$$) { $$$ }'Length of output: 168
I'm going to run an additional search using
rg
to list all occurrences ofFindByWorkspace
method definitions in the repository. This should help us verify if the implementations have been updated to the new signature with the ProjectID parameter.
🏁 Script executed:
#!/bin/bash # Search for all FindByWorkspace definitions with context. rg "func .*FindByWorkspace\(.*\)" -A10 -B10 .Length of output: 16701
Interface implementations for FindByWorkspace have been updated correctly.
- The memory implementation in
server/internal/infrastructure/memory/asset.go
accepts the new*id.ProjectID
parameter.- The Mongo implementation in
server/internal/infrastructure/mongo/asset.go
similarly reflects the updated signature.- The usecase and gateway layers (e.g., in
server/internal/usecase/interactor/asset.go
and the GraphQL loader) also pass the parameter as expected.All implementations appear to be consistent with the new interface signature.
server/internal/adapter/gql/resolver_mutation_asset.go (1)
41-62
: LGTM! UpdateAsset implementation with validation.The implementation includes proper validation of project-workspace relationship as suggested in past reviews.
server/internal/usecase/interactor/asset.go (3)
39-51
: LGTM! Method signature updated to support project filtering.The changes correctly implement project-based filtering for assets by passing the project ID to the repository layer.
88-92
: LGTM! Asset creation updated to include project association.The asset builder chain now correctly includes the project ID when creating new assets.
154-182
: Add validation for project-workspace relationship.The UploadAssetFile method should verify that the provided project belongs to the workspace before creating the asset.
func (i *Asset) UploadAssetFile(ctx context.Context, name string, zipFile *zip.File, workspace idx.ID[accountdomain.Workspace], project *id.ProjectID) (*url.URL, int64, error) { + if project != nil { + p, err := i.repos.Project.FindByID(ctx, *project) + if err != nil { + return nil, 0, fmt.Errorf("failed to validate project: %w", err) + } + if p.Workspace() != workspace { + return nil, 0, fmt.Errorf("project does not belong to workspace") + } + }server/internal/adapter/gql/resolver_query.go (1)
15-17
: LGTM! GraphQL resolver updated to support project filtering.The Assets query resolver correctly handles the new projectId parameter.
server/e2e/gql_asset_test.go (4)
16-17
: LGTM! Clear test description.The test description in the comment clearly indicates the test's purpose.
131-195
: Well-structured test coverage for project association.The test comprehensively covers:
- Asset creation with project association
- Asset retrieval by project
- Moving assets between projects
- Removing project association
- Asset deletion
197-214
: LGTM! GraphQL mutation updated correctly.The CreateAssetMutation has been properly updated to include the projectId parameter.
240-256
: LGTM! Clear implementation of asset update mutation.The updateAsset function is well-implemented with proper error handling.
server/internal/adapter/gql/resolver_mutation_project.go (1)
262-271
: LGTM! Improved transaction handling.The deferred transaction handling ensures proper cleanup in case of errors.
server/gql/workspace.graphql (2)
8-14
: LGTM! Well-structured assets query.The assets query has been properly updated to include the optional projectId parameter, maintaining backward compatibility.
15-21
: LGTM! Consistent formatting.The projects query formatting matches the assets query, maintaining consistency.
server/internal/adapter/gql/gqlmodel/models_gen.go (4)
138-138
: Verify integration of the newProjectID
field inAsset
andCreateAssetInput
structsThe addition of the optional
ProjectID
field to theAsset
andCreateAssetInput
structs appears to be correctly implemented. Ensure that all parts of the codebase that create or manipulate assets handle this new field appropriately. This includes validation, serialization, and updates to any API clients that interact with these structs.Also applies to: 186-186
1330-1337
: Ensure correct implementation ofUpdateAssetInput
andUpdateAssetPayload
structsThe introduction of
UpdateAssetInput
andUpdateAssetPayload
structs with an optionalProjectID
field extends the asset update functionality. Verify that the mutation resolvers correctly utilize these structs and that theProjectID
field is properly handled during asset updates.
1135-1149
: Verify handling of new fields in theScene
structThe addition of multiple fields to the
Scene
struct, such asProjectID
,TeamID
,Widgets
,Plugins
,NewLayers
,Stories
, andStyles
, enhances the scene's capabilities. Ensure that these fields are correctly initialized, managed, and serialized throughout the codebase. Also, verify that any associated business logic and database migrations are updated to accommodate these changes.
581-588
: Check compatibility of usingany
as a typeThe
MergedPropertyField
struct usesany
(an alias forinterface{}
introduced in Go 1.18) for theValue
field. If the project aims to maintain compatibility with versions of Go prior to 1.18, consider replacingany
withinterface{}
. Ensure consistency across all structs whereany
is used.Apply this diff if compatibility with earlier Go versions is required:
- Value any `json:"value,omitempty"` + Value interface{} `json:"value,omitempty"`✅ Verification successful
I'm going to start by checking the Go version required by the project (as specified in the
go.mod
file) to determine whether using Go 1.18'sany
alias is acceptable. This will help us know if we need backward compatibility.
🏁 Script executed:
#!/bin/bash # Check the Go version specified in go.mod grep "^go " go.modLength of output: 60
Let's get more context on the Go version used by the project by checking for Dockerfiles that might specify a Go version.
🏁 Script executed:
#!/bin/bash # Check for indications of the Go version used in Dockerfiles. grep -R "FROM golang:" .Length of output: 81
Compatibility of
any
is acceptable given the project's Go version
- The Dockerfile specifies
golang:1.23.5-alpine
, ensuring the project is built on Go 1.23.5, which fully supports theany
alias.- No backward compatibility concerns arise as Go versions prior to 1.18 are not targeted.
- The use of
any
in theMergedPropertyField
struct is consistent with the project's environment.
var pid *idx.ID[id.Project] | ||
if input.ProjectID != nil { | ||
pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pid = &pidValue | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add project-workspace validation in CreateAsset.
Similar to the validation added in UpdateAsset (from past reviews), the CreateAsset resolver should also validate the project-workspace relationship.
var pid *idx.ID[id.Project]
if input.ProjectID != nil {
pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID)
if err != nil {
return nil, err
}
+ // Validate project exists and belongs to the workspace
+ project, err := usecases(ctx).Project.FindByID(ctx, pidValue)
+ if err != nil {
+ return nil, err
+ }
+ if project.Workspace() != tid {
+ return nil, fmt.Errorf("project does not belong to specified workspace")
+ }
pid = &pidValue
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
var pid *idx.ID[id.Project] | |
if input.ProjectID != nil { | |
pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID) | |
if err != nil { | |
return nil, err | |
} | |
pid = &pidValue | |
} | |
var pid *idx.ID[id.Project] | |
if input.ProjectID != nil { | |
pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID) | |
if err != nil { | |
return nil, err | |
} | |
// Validate project exists and belongs to the workspace | |
project, err := usecases(ctx).Project.FindByID(ctx, pidValue) | |
if err != nil { | |
return nil, err | |
} | |
if project.Workspace() != tid { | |
return nil, fmt.Errorf("project does not belong to specified workspace") | |
} | |
pid = &pidValue | |
} |
func (i *Asset) Update(ctx context.Context, aid id.AssetID, pid *id.ProjectID, operator *usecase.Operator) (id.AssetID, *id.ProjectID, error) { | ||
return Run2( | ||
ctx, operator, i.repos, | ||
Usecase().Transaction(), | ||
func(ctx context.Context) (id.AssetID, *id.ProjectID, error) { | ||
asset, err := i.repos.Asset.FindByID(ctx, aid) | ||
if err != nil { | ||
return aid, pid, err | ||
} | ||
|
||
if ok := operator.IsWritableWorkspace(asset.Workspace()); !ok { | ||
return aid, pid, interfaces.ErrOperationDenied | ||
} | ||
|
||
asset.SetProject(pid) | ||
|
||
return aid, pid, i.repos.Asset.Save(ctx, asset) | ||
}, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for project-workspace relationship.
The Update method should verify that the provided project belongs to the workspace before updating the asset's project association.
func (i *Asset) Update(ctx context.Context, aid id.AssetID, pid *id.ProjectID, operator *usecase.Operator) (id.AssetID, *id.ProjectID, error) {
return Run2(
ctx, operator, i.repos,
Usecase().Transaction(),
func(ctx context.Context) (id.AssetID, *id.ProjectID, error) {
asset, err := i.repos.Asset.FindByID(ctx, aid)
if err != nil {
return aid, pid, err
}
if ok := operator.IsWritableWorkspace(asset.Workspace()); !ok {
return aid, pid, interfaces.ErrOperationDenied
}
+ if pid != nil {
+ project, err := i.repos.Project.FindByID(ctx, *pid)
+ if err != nil {
+ return aid, pid, fmt.Errorf("failed to validate project: %w", err)
+ }
+ if project.Workspace() != asset.Workspace() {
+ return aid, pid, fmt.Errorf("project does not belong to asset's workspace")
+ }
+ }
+
asset.SetProject(pid)
return aid, pid, i.repos.Asset.Save(ctx, asset)
},
)
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func (i *Asset) Update(ctx context.Context, aid id.AssetID, pid *id.ProjectID, operator *usecase.Operator) (id.AssetID, *id.ProjectID, error) { | |
return Run2( | |
ctx, operator, i.repos, | |
Usecase().Transaction(), | |
func(ctx context.Context) (id.AssetID, *id.ProjectID, error) { | |
asset, err := i.repos.Asset.FindByID(ctx, aid) | |
if err != nil { | |
return aid, pid, err | |
} | |
if ok := operator.IsWritableWorkspace(asset.Workspace()); !ok { | |
return aid, pid, interfaces.ErrOperationDenied | |
} | |
asset.SetProject(pid) | |
return aid, pid, i.repos.Asset.Save(ctx, asset) | |
}, | |
) | |
} | |
func (i *Asset) Update(ctx context.Context, aid id.AssetID, pid *id.ProjectID, operator *usecase.Operator) (id.AssetID, *id.ProjectID, error) { | |
return Run2( | |
ctx, operator, i.repos, | |
Usecase().Transaction(), | |
func(ctx context.Context) (id.AssetID, *id.ProjectID, error) { | |
asset, err := i.repos.Asset.FindByID(ctx, aid) | |
if err != nil { | |
return aid, pid, err | |
} | |
if ok := operator.IsWritableWorkspace(asset.Workspace()); !ok { | |
return aid, pid, interfaces.ErrOperationDenied | |
} | |
if pid != nil { | |
project, err := i.repos.Project.FindByID(ctx, *pid) | |
if err != nil { | |
return aid, pid, fmt.Errorf("failed to validate project: %w", err) | |
} | |
if project.Workspace() != asset.Workspace() { | |
return aid, pid, fmt.Errorf("project does not belong to asset's workspace") | |
} | |
} | |
asset.SetProject(pid) | |
return aid, pid, i.repos.Asset.Save(ctx, asset) | |
}, | |
) | |
} |
5e732cf
to
c76e941
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (2)
server/internal/adapter/gql/gqlmodel/models_gen.go (1)
906-915
: 🛠️ Refactor suggestionInconsistent type for 'ID' in 'PropertyField' struct.
The
ID
field is declared asstring
, whereas other identifier fields use the customID
type. For consistency and type safety, consider changingID
to use theID
type.Apply this diff to fix the inconsistency:
-ID string `json:"id"` +ID ID `json:"id"`server/internal/usecase/interactor/asset.go (1)
108-127
:⚠️ Potential issueAdd missing project-workspace validation.
The project-workspace validation is missing from the Update method.
As previously suggested in past reviews, add the validation before updating the asset:
if ok := operator.IsWritableWorkspace(asset.Workspace()); !ok { return aid, pid, interfaces.ErrOperationDenied } +if pid != nil { + project, err := i.repos.Project.FindByID(ctx, *pid) + if err != nil { + return aid, pid, fmt.Errorf("failed to validate project: %w", err) + } + if project.Workspace() != asset.Workspace() { + return aid, pid, fmt.Errorf("project does not belong to asset's workspace") + } +} + asset.SetProject(pid)
🧹 Nitpick comments (3)
server/internal/adapter/gql/resolver_mutation_project.go (1)
287-292
: Improve error message specificity.The current error message "Fail UploadAssetFile" doesn't indicate whether the failure was due to project ID conversion or the actual file upload.
- return nil, errors.New("Fail UploadAssetFile :" + err.Error()) + return nil, errors.New("Failed to convert project ID during asset upload: " + err.Error())server/e2e/common.go (1)
305-319
: Improve error handling inValueDump
.While the type handling has been improved, the function silently ignores JSON marshaling errors.
Consider this enhancement:
func ValueDump(val *httpexpect.Value) { raw := val.Raw() switch data := raw.(type) { case map[string]interface{}: if text, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Println(string(text)) + } else { + fmt.Printf("Error marshaling map: %v\n", err) } case []interface{}: if text, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Println(string(text)) + } else { + fmt.Printf("Error marshaling array: %v\n", err) } default: fmt.Println("Unsupported type:", reflect.TypeOf(raw)) } }server/internal/adapter/gql/gqlmodel/models_gen.go (1)
1147-1149
: Consider renaming 'NewLayers' field in 'Scene' struct for clarity.The field
NewLayers
may cause confusion regarding its purpose. If it represents the primary collection of layers in a scene, consider renaming it toLayers
for consistency. If it coexists with an existingLayers
field, a more descriptive name likeUpdatedLayers
orAdditionalLayers
might enhance clarity.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
server/e2e/common.go
(4 hunks)server/e2e/gql_asset_test.go
(7 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/seeder.go
(0 hunks)server/gql/asset.graphql
(5 hunks)server/gql/workspace.graphql
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_asset.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(7 hunks)server/internal/adapter/gql/loader_asset.go
(2 hunks)server/internal/adapter/gql/resolver_mutation_asset.go
(3 hunks)server/internal/adapter/gql/resolver_mutation_project.go
(3 hunks)server/internal/adapter/gql/resolver_query.go
(1 hunks)server/internal/adapter/gql/resolver_team.go
(1 hunks)server/internal/infrastructure/memory/asset.go
(1 hunks)server/internal/infrastructure/mongo/asset.go
(2 hunks)server/internal/infrastructure/mongo/mongodoc/asset.go
(3 hunks)server/internal/usecase/interactor/asset.go
(5 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/repo/asset.go
(1 hunks)server/pkg/asset/asset.go
(2 hunks)server/pkg/asset/builder.go
(1 hunks)server/pkg/asset/id.go
(1 hunks)server/pkg/layer/layerops/processor_test.go
(0 hunks)server/pkg/scene/builder_test.go
(0 hunks)
💤 Files with no reviewable changes (3)
- server/pkg/scene/builder_test.go
- server/pkg/layer/layerops/processor_test.go
- server/e2e/seeder.go
🚧 Files skipped from review as they are similar to previous changes (14)
- server/internal/adapter/gql/gqlmodel/convert_asset.go
- server/internal/usecase/repo/asset.go
- server/internal/adapter/gql/resolver_team.go
- server/e2e/gql_validate_geojson_test.go
- server/pkg/asset/builder.go
- server/pkg/asset/id.go
- server/gql/asset.graphql
- server/internal/infrastructure/memory/asset.go
- server/gql/workspace.graphql
- server/internal/adapter/gql/loader_asset.go
- server/pkg/asset/asset.go
- server/internal/infrastructure/mongo/mongodoc/asset.go
- server/internal/infrastructure/mongo/asset.go
- server/internal/adapter/gql/resolver_mutation_asset.go
🧰 Additional context used
📓 Learnings (1)
server/internal/adapter/gql/resolver_mutation_project.go (1)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:215-217
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project import functionality, the file names are unique and can be safely replaced uniformly using `bytes.Replace`.
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: ci-server / ci-server-lint
- GitHub Check: ci-web / ci
- GitHub Check: ci-server / ci-server-test
🔇 Additional comments (17)
server/internal/usecase/interfaces/asset.go (4)
26-31
: LGTM! TheProjectID
field is well-defined.The optional
ProjectID
field is correctly implemented as a pointer type, allowing assets to be created with or without project association.
39-39
: LGTM! TheFindByWorkspace
method signature is updated correctly.The method now accepts a
*id.ProjectID
parameter, enabling filtering of assets by project.
41-41
: LGTM! TheUpdate
method is well-designed.The method returns both
AssetID
andProjectID
, providing clear feedback about the update operation's result.
43-43
: LGTM! TheUploadAssetFile
method signature is updated correctly.The method now accepts a
*id.ProjectID
parameter, allowing project association during file upload.server/internal/adapter/gql/resolver_query.go (1)
15-16
: LGTM! TheAssets
resolver is updated correctly.The resolver now accepts and forwards the optional
projectId
parameter to the asset loader, enabling project-based filtering.server/e2e/gql_asset_test.go (3)
18-127
: LGTM! The core support test cases are comprehensive.The test cases thoroughly validate asset creation and retrieval with core support flags, including proper handling of the new
projectId
field.
131-194
: LGTM! The project association test cases are thorough.The test suite comprehensively validates the project association functionality:
- Asset creation with project association
- Project-specific asset retrieval
- Moving assets between projects
- Removing project associations
- Asset removal
215-365
: LGTM! The helper functions are well-implemented.The helper functions provide comprehensive support for testing asset management:
- Asset creation with project association
- Asset retrieval with project filtering
- Project association updates
- Asset removal
server/internal/adapter/gql/resolver_mutation_project.go (1)
262-271
: LGTM! Proper transaction handling.The transaction management is well-implemented with proper cleanup and error propagation.
server/internal/adapter/gql/gqlmodel/models_gen.go (6)
138-138
: Addition of 'ProjectID' field to 'Asset' struct is appropriate.The
ProjectID
field allows assets to be associated with projects, enhancing asset management functionality as per the PR objectives. The field is optional and correctly handled using a pointer, and the JSON tag follows the correct naming convention.
186-186
: Proper inclusion of 'ProjectID' in 'CreateAssetInput' struct.Adding
ProjectID
toCreateAssetInput
enables associating assets with projects during creation. The implementation correctly uses a pointer for the optional field, and the JSON tag is appropriately named.
581-588
: Fields added to 'MergedPropertyField' struct are properly defined.The addition of fields such as
SchemaID
,FieldID
,Value
,Type
,Links
,Overridden
,Schema
, andField
enhances the functionality ofMergedPropertyField
. The types are correctly specified, and the use ofany
for theValue
field is appropriate.
1331-1332
: 'ProjectID' correctly added to 'UpdateAssetInput' struct.Including
ProjectID
inUpdateAssetInput
allows updating the association between an asset and a project. The optional nature of the field is properly handled, and the JSON tag follows conventions.
1336-1337
: 'ProjectID' correctly added to 'UpdateAssetPayload' struct.This ensures the response after updating an asset includes the
ProjectID
, reflecting any changes made to the asset's project association.
1844-1844
: Updated 'PluginExtensionType.IsValid' method to include new extension types.The addition of new extension types like
PluginExtensionTypeStory
,PluginExtensionTypeStoryPage
,PluginExtensionTypeStoryBlock
, andPluginExtensionTypeInfoboxBlock
to the validation ensures these types are recognized as valid, aligning with the new functionalities introduced.server/internal/usecase/interactor/asset.go (2)
39-44
: LGTM! Well-structured project filtering implementation.The addition of project ID filtering in
FindByWorkspace
is clean and consistent with the existing filtering pattern.
88-92
: 🛠️ Refactor suggestionAdd project-workspace validation before asset creation.
While setting the project ID is implemented correctly, you should validate that the project belongs to the workspace before creating the asset.
Add this validation after the workspace check:
if !operator.IsWritableWorkspace(ws.ID()) { return nil, interfaces.ErrOperationDenied } + +if inp.ProjectID != nil { + project, err := i.repos.Project.FindByID(ctx, *inp.ProjectID) + if err != nil { + return nil, fmt.Errorf("failed to validate project: %w", err) + } + if project.Workspace() != ws.ID() { + return nil, fmt.Errorf("project does not belong to workspace") + } +}
@@ -181,7 +181,7 @@ func (r *mutationResolver) ExportProject(ctx context.Context, input gqlmodel.Exp | |||
Key: "id", | |||
Desc: true, | |||
} | |||
assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), nil, sort, page, getOperator(ctx)) | |||
assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), nil, nil, sort, page, getOperator(ctx)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Filter assets by project ID during export.
The current implementation exports all assets in the workspace. Consider filtering assets by project ID to ensure only relevant assets are exported.
- assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), nil, nil, sort, page, getOperator(ctx))
+ assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), &prj.ID(), nil, sort, page, getOperator(ctx))
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), nil, nil, sort, page, getOperator(ctx)) | |
assets, _, err := usecases(ctx).Asset.FindByWorkspace(ctx, prj.Workspace(), &prj.ID(), nil, sort, page, getOperator(ctx)) |
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { | ||
if actualData, ok := actual.Raw().(map[string]interface{}); ok { | ||
return JSONEqRegexpInterface(t, actualData, expected) | ||
} | ||
return false | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance error handling and type support in JSONEqRegexpValue
.
The function has limited type support and lacks error handling:
- Only handles map types, but could receive arrays or other JSON-compatible values
- No error handling for JSON marshaling failures
- Missing documentation explaining the function's purpose
Consider this improved implementation:
+// JSONEqRegexpValue compares the JSON representation of an httpexpect.Value against a regexp pattern.
+// Returns true if the pattern matches, false otherwise.
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool {
- if actualData, ok := actual.Raw().(map[string]interface{}); ok {
+ actualData := actual.Raw()
+ if actualData == nil {
+ t.Error("actual value is nil")
+ return false
+ }
+ switch actualData.(type) {
+ case map[string]interface{}, []interface{}, string, float64, bool:
return JSONEqRegexpInterface(t, actualData, expected)
+ default:
+ t.Errorf("unsupported type for JSON comparison: %T", actualData)
+ return false
}
- return false
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { | |
if actualData, ok := actual.Raw().(map[string]interface{}); ok { | |
return JSONEqRegexpInterface(t, actualData, expected) | |
} | |
return false | |
} | |
// JSONEqRegexpValue compares the JSON representation of an httpexpect.Value against a regexp pattern. | |
// Returns true if the pattern matches, false otherwise. | |
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { | |
actualData := actual.Raw() | |
if actualData == nil { | |
t.Error("actual value is nil") | |
return false | |
} | |
switch actualData.(type) { | |
case map[string]interface{}, []interface{}, string, float64, bool: | |
return JSONEqRegexpInterface(t, actualData, expected) | |
default: | |
t.Errorf("unsupported type for JSON comparison: %T", actualData) | |
return false | |
} | |
} |
@@ -129,7 +151,7 @@ func (i *Asset) Remove(ctx context.Context, aid id.AssetID, operator *usecase.Op | |||
) | |||
} | |||
|
|||
func (i *Asset) UploadAssetFile(ctx context.Context, name string, zipFile *zip.File, workspace idx.ID[accountdomain.Workspace]) (*url.URL, int64, error) { | |||
func (i *Asset) UploadAssetFile(ctx context.Context, name string, zipFile *zip.File, workspace idx.ID[accountdomain.Workspace], project *id.ProjectID) (*url.URL, int64, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add project-workspace validation in UploadAssetFile.
Similar to other methods, project-workspace validation is needed before creating the asset.
Add the validation before creating the asset:
func (i *Asset) UploadAssetFile(ctx context.Context, name string, zipFile *zip.File, workspace idx.ID[accountdomain.Workspace], project *id.ProjectID) (*url.URL, int64, error) {
+ if project != nil {
+ p, err := i.repos.Project.FindByID(ctx, *project)
+ if err != nil {
+ return nil, 0, fmt.Errorf("failed to validate project: %w", err)
+ }
+ if p.Workspace() != workspace {
+ return nil, 0, fmt.Errorf("project does not belong to workspace")
+ }
+ }
Also applies to: 178-182
Overview
Add the association of the project to the asset.
What I've done
I have added the project parameter to the asset.
Added updateAsset, which allows you to change the project.
You can also specify null to make it a workspace file.
The changes to the API are as follows:
The projectId parameter for createAsset is optional, while teamId remains required as before.
teamId is necessary because, if the owner is not present, the data would remain as unmanaged.
For the Query, when both teamId and projectId are specified, the search will be performed based on projectId (and teamId will be ignored).
The data model for the asset is as shown in the diagram below.
![スクリーンショット 2025-02-05 19 39 49](https://private-user-images.githubusercontent.com/17220073/409946774-b0978637-202f-4d29-bec8-5ad230d3f246.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzg5NjMzMzEsIm5iZiI6MTczODk2MzAzMSwicGF0aCI6Ii8xNzIyMDA3My80MDk5NDY3NzQtYjA5Nzg2MzctMjAyZi00ZDI5LWJlYzgtNWFkMjMwZDNmMjQ2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMDclMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjA3VDIxMTcxMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTgzOWE4OWY1OWUwMGQwZjA4NGQ0YWNlOTk3MWQxMWNhZWUzNDk3ZjRlZTA1MWVkNDdlNDcxYzEzZmFiODMxNTImWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.Idzef0MCFJvbeqcToM6cbxXQOsbOFtibWYNSk0fQXY0)
What I haven't done
How I tested
e2e test
https://github.com/reearth/reearth-visualizer/pull/1410/files#diff-be135cdb0bc1a5b8a184c356c9664cee68bcc8220f9d2097ad6b5348ac25c425R131
Which point I want you to review particularly
Memo
Fix the front-end query here
web/src/services/gql/queries/asset.ts
Summary by CodeRabbit
New Features
projectId
parameter in various asset-related API methods for improved project association.Refactor / Removed Features