-
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): storytelling ga support [VIZ-1180] #1425
Conversation
WalkthroughThis pull request refactors server initialization and configuration in end-to-end tests, updating function signatures and renaming functions to streamline setups. It introduces a dedicated disabled authentication configuration variable and consolidates server start logic. In addition, the changes add new Google Analytics tracking fields ( Changes
Sequence Diagram(s)sequenceDiagram
participant T as Test
participant S as Server Init
participant NS as New Server (startServer)
T->>S: Call ServerAndRepos/Server (with seeder)
S->>NS: Invoke new initialization using disabledAuthConfig
NS-->>S: Return HTTP Expect, Repo, Gateway
S-->>T: Return initialized server components
sequenceDiagram
participant C as Client
participant G as GraphQL Resolver
participant U as StoryTelling Use Case
participant DB as Database
C->>G: Send UpdateStory mutation (with enableGa, trackingId)
G->>U: Forward update request with GA fields
U->>DB: Update Story record with new GA settings
DB-->>U: Return updated Story data
U-->>G: Pass updated data
G-->>C: Respond with updated Story
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ 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 ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
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: 6
🔭 Outside diff range comments (4)
server/internal/adapter/gql/resolver_mutation_project.go (1)
212-345
: 🛠️ Refactor suggestionCheck for silent error handling and potential edge cases.
- At line 214: converting
TeamID
with_ := accountdomain.WorkspaceIDFrom(...)
discards any error, risking silent failures for invalid workspace IDs.- At line 223: ignoring the error on
unmarshalProject
.- Multiple sections assume successful type assertions (e.g.,
prj.ImageURL() != nil
).- Potential incomplete rollback if partial imports fail in the middle.
Consider properly handling or surfacing all possible errors to prevent corrupted imports.
server/pkg/layer/encoding/exporter.go (1)
37-42
:⚠️ Potential issueCritical: Encode method returns nil without performing any operations.
The
Encode
method has been simplified to return nil without performing any encoding operations. This could lead to silent failures in dependent systems expecting encoded data.Please either:
- Document why encoding is no longer needed
- Implement the new encoding logic
- Add a deprecation notice if this is intentional
server/internal/usecase/repo/asset.go (1)
18-26
: 💡 Verification agent🧩 Analysis chain
Breaking change: Asset interface updated to include project context.
The
FindByWorkspaceProject
method replacesFindByWorkspace
, adding project ID as a required parameter. This is a breaking change that requires updates in all implementations.
🏁 Script executed:
#!/bin/bash # Description: Check for implementations that need updating # Search for Asset interface implementations ast-grep --pattern 'type $_ struct { $$$ } func ($_ $_) FindByWorkspace' # Search for existing calls to FindByWorkspace rg "FindByWorkspace\(" --type goLength of output: 3240
Action Required: Update all Asset interface implementations and callers to use
FindByWorkspaceProject
.The Asset interface in
server/internal/usecase/repo/asset.go
(lines 18–26) now defines the method:FindByWorkspaceProject(context.Context, accountdomain.WorkspaceID, *id.ProjectID, AssetFilter) ([]*asset.Asset, *usecasex.PageInfo, error)This replaces the old
FindByWorkspace
method by requiring an additional project identifier. Our verification found that several callers in the codebase (e.g., inserver/internal/adapter/gql/loader_asset.go
andserver/internal/adapter/gql/resolver_team.go
, among others) are still invokingFindByWorkspace
. This breaking change means that all existing implementations and calls toFindByWorkspace
must be updated to handle the new project context parameter to maintain compatibility.
- Files requiring update:
server/internal/adapter/gql/loader_asset.go
server/internal/adapter/gql/resolver_team.go
- (Other usages in the repository should be reviewed similarly)
Please refactor these implementations and invocations accordingly.
server/internal/adapter/gql/resolver_team.go (1)
27-34
:⚠️ Potential issueFix method name mismatch.
The method call on line 28 uses
FindByWorkspace
, but the interface has been renamed toFindByWorkspaceProject
.Apply this diff to fix the method name:
- return loaders(ctx).Asset.FindByWorkspace(ctx, obj.ID, projectID, nil, nil, &gqlmodel.Pagination{ + return loaders(ctx).Asset.FindByWorkspaceProject(ctx, obj.ID, projectID, nil, nil, &gqlmodel.Pagination{
🧹 Nitpick comments (30)
server/internal/usecase/interfaces/project.go (1)
46-47
: Consider reusing existing GA implementation.The
UpdateProjectParam
struct already has GA-related fields (EnableGa
andTrackingID
). Consider reusing this implementation pattern for the Story component to maintain consistency.server/e2e/gql_asset_test.go (2)
124-179
: Meaningful project association tests.The
TestAssociateProjectGetAssets
function thoroughly checks project-based asset creation, retrieval, reassignment, and removal. This adds valuable coverage for multi-project scenarios. If time permits, consider also testing invalid or unprivileged project moves.
200-223
: Graceful error handling increateAsset
.This helper properly open/closes the file and sets up a multipart request. Consider adding an optional check or user-facing error message if the file is too large or missing, but otherwise this is good test support code.
server/internal/adapter/gql/resolver_mutation_project.go (1)
178-183
: Remove or restore commented-out code.This commented block for converting a secondary project ID is unused. Either delete it to keep the codebase clean or explain why it remains.
- // pid2, err := gqlmodel.ToID[id.Project](gqlmodel.ID(prj.ID().String())) - // if err != nil { - // return nil, err - // }server/e2e/gql_project_export_import_test.go (3)
19-19
: Helpful test instructions.Comment clarifies how to run tests specifically for
TestProjectExportImport
, potentially beneficial to new contributors.
21-63
: Test flow covers exporting and importing thoroughly.
- Exports project → Imports project → Checks resulting structure.
- Removes temporary file on completion (line 58).
Consider adding a test for invalid or missing files to ensure graceful error handling.
172-186
: Scene retrieval can fail silently.
getScene
expects a valid scene node in the response. If the node is missing or an error occurs, the test might be misled. Consider adding explicit checks for GraphQL errors or null nodes.server/e2e/seeder.go (8)
47-47
: Project description auto-derived from name.
pDesc = pName + " desc"
is a simple pattern. If more dynamic or localized descriptions are needed in the future, consider a more robust approach.
51-55
: Global IDs for layers and storytelling.Defining global IDs is convenient for e2e tests. Ensure they won't conflict with other tests or seeders if parallel testing is introduced.
73-86
: Language-specific seeding logic.
baseSeederWithLang
parallelsbaseSeeder
but adds a user language preference. This approach is consistent, though you might unify them to reduce duplication if needed.
169-183
: Full seeder duplicates setup steps.
fullSeeder
redoes part ofbaseSeeder
. Evaluate merging them or callingbaseSeeder
first to keep code DRY.
283-312
: Story and page creation logic.Allows test environment to have a story with a single page. This is straightforward. Consider test coverage for multiple pages, blocks, and page transitions if needed.
314-338
: Page creation with a single story block.Similar to the story logic, it ensures minimal coverage. Expand for multi-block scenarios if thorough coverage is desired.
370-393
: Layer style creation injection.The style JSON is partially hardcoded. For more flexible tests, consider parameterizing or randomizing style data.
395-448
: Adding a simple NLS layer with minimal config.Uses partial JSON for testing a layer’s environment. Check potential for testing advanced or more complex configurations to broaden coverage.
server/e2e/gql_storytelling_test.go (3)
15-55
: Solid test coverage for basic story CRUD.Adding a check for the updated story title after
updateStory
might further strengthen this test's coverage.
208-229
: Property value updates are covered.Consider adding tests for edge cases (e.g., invalid property types) to ensure robust coverage.
231-413
: Thorough publishing test.You might add a scenario testing a story with
enableGa
set to true to confirm GA fields appear in the published output.server/pkg/asset/asset.go (1)
34-40
: Consider adding validation in SetProject method.While the getter and setter methods are implemented correctly, consider adding validation in
SetProject
to ensure data integrity.func (a *Asset) SetProject(project *ProjectID) { + if project != nil { + if err := project.Validate(); err != nil { + return + } + } a.project = project }server/internal/infrastructure/mongo/mongodoc/asset.go (1)
36-40
: LGTM! Consider consolidating string conversion logic.The project ID conversion logic is well-implemented with proper error handling. However, consider extracting the string conversion logic into a helper function to avoid duplication.
Consider creating a helper function:
func projectIDToString(id *idx.ID[id.Project]) *string { if id == nil { return nil } s := id.String() return &s }Also applies to: 65-72
server/internal/adapter/gql/resolver_mutation_asset.go (1)
41-62
: Consider standardizing error variable names.The implementation looks good, but there's an inconsistency in error variable naming (
err
vserr2
).- a, p, err2 := usecases(ctx).Asset.Update(ctx, aid, pid, getOperator(ctx)) - if err2 != nil { - return nil, err2 + a, p, err := usecases(ctx).Asset.Update(ctx, aid, pid, getOperator(ctx)) + if err != nil { + return nil, errserver/pkg/storytelling/story_bulider.go (1)
129-132
: Consider adding validation for tracking ID.The tracking ID should be validated to ensure it follows the Google Analytics format (e.g., 'UA-XXXXX-Y' or 'G-XXXXXXXX').
func (b *StoryBuilder) TrackingID(trackingID string) *StoryBuilder { + if trackingID == "" { + return b + } + // Validate GA4 format (G-XXXXXXXX) or Universal Analytics format (UA-XXXXX-Y) + if !regexp.MustCompile(`^(G-[A-Z0-9]+|UA-\d+-\d+)$`).MatchString(trackingID) { + return b + } b.s.trackingID = trackingID return b }server/internal/adapter/gql/gqlmodel/convert_project.go (1)
127-137
: Consider enhancing error handling in JSON conversion.The current implementation silently returns nil on JSON conversion errors. Consider:
- Logging errors for debugging
- Returning errors to the caller for better error handling
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { var p ProjectExport bytes, err := json.MarshalIndent(data, "", " ") if err != nil { + log.Printf("Failed to marshal project data: %v", err) return nil } if err := json.Unmarshal(bytes, &p); err != nil { + log.Printf("Failed to unmarshal project data: %v", err) return nil } return &p }server/internal/infrastructure/mongo/asset.go (1)
96-100
: Consider simplifying the URL filter logic.The URL filter construction could be simplified by directly assigning to the filter map, similar to how other filters are handled.
- if andFilter, ok := mongox.And(filter, "url", bson.M{ - "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, - }).(bson.M); ok { - filter = andFilter - } + filter["url"] = bson.M{"$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}}server/e2e/common.go (2)
45-45
: Consider using a builder pattern for repository initialization.The repository initialization code could be more maintainable using a builder pattern.
Example refactor:
-func initRepos(t *testing.T, useMongo bool, seeder Seeder) (repos *repo.Container, file gateway.File) { +type repoBuilder struct { + t *testing.T + useMongo bool + seeder Seeder +} + +func newRepoBuilder(t *testing.T) *repoBuilder { + return &repoBuilder{t: t} +} + +func (b *repoBuilder) withMongo() *repoBuilder { + b.useMongo = true + return b +} + +func (b *repoBuilder) withSeeder(s Seeder) *repoBuilder { + b.seeder = s + return b +} + +func (b *repoBuilder) build() (repos *repo.Container, file gateway.File) { ctx := context.Background() - if useMongo { + if b.useMongo { // ... rest of the code } }Also applies to: 51-71
73-82
: Consider adding error handling for file initialization.The file initialization in
initGateway
could benefit from explicit error handling.Example refactor:
func initGateway() *gateway.Container { if fr == nil { + file, err := fs.NewFile(afero.NewMemMapFs(), "https://example.com/") + if err != nil { + panic(fmt.Sprintf("failed to initialize file gateway: %v", err)) + } return &gateway.Container{ - File: lo.Must(fs.NewFile(afero.NewMemMapFs(), "https://example.com/")), + File: file, } } return &gateway.Container{ File: *fr, } }server/internal/usecase/interactor/project.go (1)
627-649
: Improve error handling in AddZipAsset.The
AddZipAsset
function should provide more specific error messages and consider using structured error types.Example refactor:
func AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error { fileName := strings.TrimPrefix(path, "/assets/") stream, err := file.ReadAsset(ctx, fileName) if err != nil { - return nil // skip if external URL + // Skip if external URL, but log for debugging + fmt.Printf("Skipping external URL: %s, err: %v\n", path, err) + return nil } defer func() { if cerr := stream.Close(); cerr != nil { - fmt.Printf("Error closing file: %v\n", cerr) + fmt.Printf("Error closing file %s: %v\n", fileName, cerr) } }() zipEntryPath := fmt.Sprintf("assets/%s", fileName) zipEntry, err := zipWriter.Create(zipEntryPath) if err != nil { - return err + return fmt.Errorf("failed to create zip entry for %s: %w", fileName, err) } _, err = io.Copy(zipEntry, stream) if err != nil { _ = stream.Close() - return err + return fmt.Errorf("failed to copy asset %s to zip: %w", fileName, err) } return nil }server/internal/usecase/interactor/nlslayer.go (1)
1109-1134
: Consider using a batch operation for feature creation.The current implementation creates features one by one in a loop. For large feature collections, this could be inefficient.
Consider refactoring to use a batch operation:
-features := make([]nlslayer.Feature, 0) -for _, featureJSON := range nlsLayerJSON.SketchInfo.FeatureCollection.Features { +features := make([]nlslayer.Feature, 0, len(nlsLayerJSON.SketchInfo.FeatureCollection.Features)) +for _, featureJSON := range nlsLayerJSON.SketchInfo.FeatureCollection.Features {server/gql/asset.graphql (1)
5-5
: Consider making projectId non-nullable for better data consistency.The
projectId
field is currently optional. If assets should always be associated with a project, consider making this field required.- projectId: ID + projectId: ID!Also applies to: 24-24
server/Makefile (1)
85-91
: Consider making the container name configurable and add error handling.While the new target is useful, there are a few improvements that could make it more robust:
- The container name is hardcoded which could break if the project name changes.
- No error handling if the container stop fails.
Consider this improvement:
+MONGO_CONTAINER ?= reearth-visualizer-reearth-mongo-1 run-reset: - docker stop reearth-visualizer-reearth-mongo-1 + docker stop $(MONGO_CONTAINER) || true rm -rf ../mongo rm -rf data make run-db make mockuser
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
server/e2e/test.zip
is excluded by!**/*.zip
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 (107)
server/Makefile
(3 hunks)server/e2e/common.go
(7 hunks)server/e2e/dataset_export_test.go
(0 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_custom_property_test.go
(1 hunks)server/e2e/gql_layer_test.go
(0 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_nlslayer_test.go
(0 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 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/storytelling.graphql
(3 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_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_plugin.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_scene.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 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
(9 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
(3 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 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/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/layer.go
(0 hunks)server/internal/usecase/interactor/nlslayer.go
(3 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/scene.go
(5 hunks)server/internal/usecase/interactor/scene_test.go
(0 hunks)server/internal/usecase/interactor/storytelling.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 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/nlslayer/feature.go
(0 hunks)server/pkg/nlslayer/feature_test.go
(1 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)server/pkg/scene/builder/story.go
(1 hunks)server/pkg/scene/builder_test.go
(0 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)web/src/services/api/propertyApi/utils.ts
(1 hunks)
⛔ Files not processed due to max files limit (6)
- web/src/services/api/sceneApi.ts
- web/src/services/gql/fragments/dataset.ts
- 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/scene.ts
💤 Files with no reviewable changes (47)
- server/pkg/nlslayer/feature.go
- server/internal/usecase/interactor/scene_test.go
- server/internal/adapter/gql/context.go
- server/internal/adapter/gql/gqlmodel/models.go
- server/pkg/scene/builder/builder_test.go
- server/internal/adapter/gql/gqlmodel/convert.go
- server/e2e/gql_nlslayer_test.go
- server/internal/usecase/interactor/project_test.go
- server/internal/infrastructure/mongo/mongodoc/scene.go
- server/internal/adapter/gql/resolver_property_test.go
- server/internal/adapter/gql/gqlmodel/convert_tag.go
- server/gqlgen.yml
- server/internal/adapter/gql/gqlmodel/convert_dataset.go
- server/e2e/gql_project_export_test.go
- server/pkg/layer/layerops/processor_test.go
- server/internal/adapter/gql/gqlmodel/convert_plugin.go
- server/internal/adapter/gql/gqlmodel/convert_scene.go
- server/e2e/gql_layer_test.go
- server/pkg/scene/builder/encoder_test.go
- server/e2e/dataset_export_test.go
- server/gql/cluster.graphql
- server/internal/usecase/interactor/layer.go
- server/pkg/scene/builder_test.go
- server/gql/property.graphql
- server/gql/dataset.graphql
- server/internal/adapter/gql/gqldataloader/dataloader.go
- server/internal/adapter/gql/gqlmodel/convert_layer.go
- server/internal/adapter/gql/loader.go
- server/internal/adapter/gql/resolver_mutation_scene.go
- server/internal/adapter/gql/resolver_scene.go
- server/internal/adapter/gql/resolver_dataset.go
- server/internal/adapter/gql/resolver_layer.go
- server/internal/adapter/gql/resolver_mutation_tag.go
- server/internal/adapter/gql/resolver_property.go
- server/internal/adapter/gql/resolver_dataset_schema.go
- server/internal/adapter/gql/resolver_tag.go
- server/gql/tag.graphql
- server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
- server/internal/adapter/gql/gqldataloader/tagloader_gen.go
- server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
- server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
- server/internal/adapter/gql/loader_dataset.go
- server/gql/layer.graphql
- server/internal/adapter/gql/resolver_mutation_layer.go
- server/internal/adapter/gql/loader_tag.go
- server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
- server/internal/adapter/gql/resolver_mutation_dataset.go
✅ Files skipped from review due to trivial changes (2)
- server/pkg/scene/builder/encoder.go
- server/go.mod
🧰 Additional context used
🧠 Learnings (2)
server/internal/adapter/gql/resolver_mutation_project.go (2)
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`.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: ci-server / ci-server-test
- GitHub Check: Redirect rules - reearth-web
- GitHub Check: Header rules - reearth-web
- GitHub Check: Pages changed - reearth-web
🔇 Additional comments (114)
server/internal/usecase/interfaces/project.go (2)
14-14
: LGTM! Import addition improves type safety.The addition of the
idx
package import supports the use of strongly-typed IDs, which is a good practice for type safety.
74-75
: LGTM! Method signature changes improve clarity and type safety.The changes enhance the interface by:
- Using more descriptive method names with "Data" suffix
- Replacing string with strongly-typed workspace ID
- Adding operator parameter for proper authorization
server/pkg/scene/builder/builder.go (3)
26-38
: LGTM: Clean removal of encoder-related code.The removal of the encoder field and its initialization is consistent with the broader refactoring effort mentioned in the summary.
Also applies to: 40-57
91-126
: LGTM: GA parameters correctly propagated.The addition of Google Analytics parameters (enableGa, trackingId) is consistent with the PR objectives, and they are properly propagated through the build methods.
Also applies to: 128-163
165-182
:❓ Verification inconclusive
Verify the impact of removing layers parameter from sceneJSON.
The removal of the layers parameter from sceneJSON call aligns with the encoder removal. However, ensure that layer data is still properly handled in the sceneJSON method.
🏁 Script executed:
#!/bin/bash # Description: Check if sceneJSON method still handles layer data correctly # Search for sceneJSON method definition and its usage of layer data ast-grep --pattern 'func $_(b *Builder) sceneJSON($_, $_, $_, $_, $_, $_) (*sceneJSON, error) { $$$ }'Length of output: 106
Manual Verification Required:
The previous ast-grep check did not return any output, so please run the above script to manually inspect the definition ofsceneJSON
inserver/pkg/scene/builder/builder.go
. In particular, verify whether the method includes logic to process or incorporate layer data internally now that the layer parameter has been removed from the call.
🏁 Script executed:
#!/bin/bash # Verify the definition of sceneJSON in the file and inspect if it includes any processing of layer data. rg -n "func (b \\*Builder) sceneJSON" -A 15 server/pkg/scene/builder/builder.goLength of output: 80
Manual Verification Required: Confirm Layer Data Handling in sceneJSON
The removal of the layers parameter in the call tosceneJSON
appears to align with the removal of encoder dependencies. However, our automated checks did not yield any output to confirm whether the implementation ofsceneJSON
—presumably defined inserver/pkg/scene/builder/builder.go
or elsewhere—now correctly incorporates layer data.
• Please manually inspect the definition ofsceneJSON
to ensure that any necessary layer data processing is still performed internally.
• Adjust the implementation if layer data is required but no longer being explicitly passed via a parameter.server/internal/usecase/interactor/asset.go (4)
39-44
: Ensure consistent usage of project filter.The newly introduced
FindByWorkspaceProject
method effectively includespid
as a filter. Verify that this function is consistently called elsewhere and that callers handle the possibility of a nilpid
correctly.
154-154
: New import function signature looks good.The updated signature includes a
project
parameter, aligning with project-based asset management. Keep watch for potential oversights in verifying that the provided project ID is valid and that the caller has sufficient permissions.
166-169
: Confirm usage ofrealName
for the file path.Using
realName
instead of the originalzipFile.Name
is fine if that aligns with your domain requirements. Ensure it does not unintentionally overwrite files with common names and that any required sanitization or uniqueness checks are performed.
181-182
: Properly set project and asset name.Assigning
project
and derivingname
frompath.Base(realName)
is consistent and clear. This design supports flexible naming without leaking directory information.server/internal/adapter/gql/resolver_query.go (1)
15-16
: Align resolver call with new project-based asset retrieval.The updated
Assets
method now accepts an optionalprojectId
but callsFindByWorkspace(...)
under the hood. Confirm that this loader indeed supports a project-based query (possibly via an internal rename or bridging). Otherwise, consider calling a method explicitly namedFindByWorkspaceProject
for clarity.server/e2e/gql_asset_test.go (8)
15-16
: Test label change detected.The rename to
TestCoreSupportGetAssets
clarifies the purpose of testing asset retrieval with varyingcoreSupport
flags.
17-120
: Potential mismatch between test scenario and final assertions.Within
TestCoreSupportGetAssets
, multiple assets with differentcoreSupport
values are created, but at line 117 all returned assets are asserted to havecoreSupport == true
. Consider verifying or filtering bycoreSupport
in the query to avoid confusion or unintended failures.
122-122
: New test directive comment.Adding
// go test -v -run TestAssociateProjectGetAssets
is a helpful reminder for targeted test runs.
181-199
: GraphQL mutation now accommodates project IDs.Adding the
projectId
field keeps creation logic flexible. Confirm that clients passing a nilprojectId
won’t cause unintended consequences.
[approve]
224-241
: Update mutation coverage.
updateAsset
effectively tests reassigning an asset to a new project or removing its project. Confirm that negative or unauthorized scenarios are handled upstream or with additional test coverage.
242-257
: Asset removal workflow.
removeAsset
is straightforward, ensuring that the desired asset is removed after test usage. This helps maintain a clean setup/teardown policy in e2e tests.
258-286
: Temporary file creation for asset upload testing.
createAssetFromFileData
handles temporary file creation and cleanup. Great approach for varied input data testing. Just ensure large or malformed data is gracefully handled or validated.
287-351
: UpdatedgetAssets
query with optionalprojectId
.Including
projectId
in the query supports fine-grained filtering in the e2e tests. This ensures consistent alignment with the new server-side project-based asset fetching logic.server/internal/adapter/gql/resolver_mutation_project.go (9)
10-12
: Imports look appropriate.The newly added packages (
net/url
andpath
) are relevant to parse asset URLs and handle file paths. No immediate concerns.
130-130
: Watch out for special characters in file names.Using the string form of
input.ProjectID
directly in the zip file name could lead to malformed or unexpected filenames if the project ID contains special or unsafe characters. Consider sanitizing the string to avoid filesystem issues.
155-155
: Ensure all exported data is validated thoroughly.The call to
Project.ExportProjectData
is critical. Ensure validations around the project data are handled properly, especially edge cases where the project might be incomplete or contain invalid references.
189-195
: Validate parsed asset filenames.When extracting filename using
path.Base(parsedURL.Path)
, ensure that any special or leading/trailing characters are handled. Filenames could conflict with existing assets or contain malicious patterns.
197-199
: Data map population seems coherent.Storing the project, plugins, and schemas in
data
is clear. Ensure these keys match the reading logic in downstream code (e.g.,unmarshalProject
,unmarshalPluginsScene
, etc.).
333-345
: Guard against invalid JSON structure.
replaceOldSceneID
uses a type assertion:oldSceneData, _ := jsonData["scene"].(map[string]interface{}) oldSceneID := oldSceneData["id"].(string)
If
jsonData["scene"]
or"id"
isn't present or is of the wrong type, this could panic. Recommend gracefully checking.
346-354
: Verify presence of “project” field in JSON.
unmarshalProject
callsjsonData["project"].(map[string]interface{})
without confirming it exists or is the correct type. A missing or different type would return nil or cause an error. Add validations to handle unexpected structures.
356-370
: Handle missing or malformed asset entries.
unmarshalAssets
similarly convertsjsonData["assets"]
tomap[string]interface{}
; a missing key or unexpected structure can cause issues. Consider checking each asset entry type before usage.
372-382
: Check for optional plugin & schema fields.
unmarshalPluginsScene
callsjsonData["plugins"].([]interface{})
andjsonData["schemas"].([]interface{})
. If these fields do not exist or are incorrectly typed, it may cause runtime issues.server/e2e/gql_project_export_import_test.go (5)
4-8
: Imported packages are consistent with new functionality.All required imports for JSON handling, HTTP, etc. are properly referenced. No issues found.
12-13
: ID usage is correct.New references to
id
andidx
fit seamlessly into the test logic.
65-101
: Comparison utilities appear robust.
compareValue
,convertLine
, and ignoring certain fields (e.g., IDs) is a good approach. However, consider verifying that ignoring these fields won’t mask legitimate mismatches.
121-139
: Export function logic is sound.The test fetches the exported zip from the server, writes it locally, and then ensures no error. This design effectively simulates a real export scenario.
141-169
: Import process is thorough.
importProject
uses multipart file uploads and checks theprojectData
field. For completeness, consider verifying error scenarios (e.g., invalid file).server/e2e/seeder.go (8)
5-9
: Additional imports used correctly.Rand, hex, JSON, and other packages align with new feature expansions (UUID generation, JSON manipulation, etc.). No concerns.
49-49
: Mocked time set to a fixed date.Using
now = time.Date(2022, ...)
is acceptable for stable test environments but remember to reset or mock carefully if needed in code beyond tests.
58-71
: Base seeder logic extended with file gateway support.
baseSeeder
now callsbaseSetup
with a new file parameter. This is beneficial for integrated asset seeding.
88-123
: Asset addition in base setup.Calls
addAsset("test.png", ...)
unconditionally. Iftest.png
doesn’t exist, the seeder fails. Consider verifying or providing a default fallback to ensure consistent seeding.
232-281
: Widget creation approach is sound.
- Creates plugin extension, property, and widget entries.
- Ensures references are saved in scene.
Check concurrency or repeated calls if needed to avoid duplication.
340-368
: Block creation with plugin extension.Ensures that the
extension
is the correct type. This pattern is consistent with the plugin-based architecture.
450-510
: GeoJSON injection is well structured.The function
addGeoJson
manually parses geometry and updates the layer’s sketch. This approach is fine for testing. For production or more complex E2E, consider validating geometry from external or random sources.
512-527
: UUID generation is straightforward.
generateUUID
is a standard approach for test IDs. No concerns.server/e2e/gql_storytelling_test.go (3)
57-152
: Comprehensive page CRUD testing.Test covers creation, duplication, ordering, and deletion of pages thoroughly.
154-206
: Good coverage of block CRUD.The test scenario ensures blocks can be created, ordered, and removed without issues.
869-901
: Simplified removeBlock function.Ensures the removed block no longer appears in the page, aligning well with expected behavior.
server/internal/adapter/gql/gqlmodel/models_gen.go (11)
138-138
: ProjectID field for Asset is a good addition.Helps tie assets to specific projects, enabling better organization.
185-186
: Optional projectId in CreateAssetInput is consistent.Allows assets to be tied to a project at creation time without breaking older workflows.
581-588
: Enhanced MergedPropertyField.Ensure all references (SchemaID, FieldID) are consistently populated to avoid merge-time null pointers.
906-915
: Changed ID type from ID to string in PropertyField.Confirm this change is intentional. It may cause type mismatches if the rest of the code expects an ID type.
919-921
: Consistent linking fields in PropertyFieldLink.Optional datasetId plus mandatory datasetSchema references look structurally sound.
1135-1149
: Scene struct expansions.Referencing project, team, and property directly promotes clarity and reduces fragmentation.
1221-1222
: GA fields in Story model.Straightforward extension for analytics tracking.
1332-1334
: UpdateAssetInput now includes projectId.Keeps input aligned with asset creation logic regarding project association.
1337-1339
: UpdateAssetPayload reflects projectId assignment.Maintains consistency with the input changes.
1459-1460
: GA fields in UpdateStoryInput.Allows toggling and setting GA tracking configuration for an existing story.
1848-1848
: Extended plugin extension types.Ensures coverage for the newly introduced story/infobox extension points.
server/e2e/gql_me_test.go (1)
8-8
: Simplified server initialization.By removing explicit config, the test now relies on default or embedded settings, reducing potential config drift.
server/e2e/ping_test.go (1)
9-9
:❓ Verification inconclusive
Verify test coverage with simplified server initialization.
The server initialization has been simplified to use
ServerPingTest
. While this makes the test more concise, ensure that the new initialization maintains the same test coverage for CORS and authentication behaviors that were previously explicitly configured.
🏁 Script executed:
#!/bin/bash # Description: Check if ServerPingTest maintains the same configuration as before # Search for ServerPingTest implementation rg -A 10 "func ServerPingTest" # Check for CORS and auth configuration in the implementation rg -l "Origins.*example.com" "server/e2e" rg -l "AuthSrv" "server/e2e"Length of output: 692
Confirm CORS and Authentication Coverage in Simplified Server Initialization
The
ServerPingTest
function inserver/e2e/common.go
now initializes the server usingdisabledAuthConfig
, which does not explicitly configure CORS or authentication. In contrast, the previous explicit settings (as seen inServerMockTest
) include values forOrigins
(e.g.,"https://example.com"
) andAuthSrv
. Please verify that tests for CORS and authentication are still exercised—either via separate tests (like those usingServerMockTest
) or by extending the current test suite—to maintain comprehensive coverage.server/pkg/layer/encoding/exporter.go (1)
10-13
:❓ Verification inconclusive
Verify removal of encoding functionality.
The
Encoder
field has been removed from theExporter
struct. This appears to be a breaking change that could affect layer export functionality.
🏁 Script executed:
#!/bin/bash # Description: Check for potential impacts of removing the Encoder # Search for usages of Exporter.Encode rg -A 5 "Exporter.*Encode" # Search for potential alternatives to encoding rg -l "type.*Encoder" "server/pkg/layer"Length of output: 594
Confirm Exporter.Encode Behavior and Downstream Impact
I verified that the
Encoder
field has indeed been removed from theExporter
struct inserver/pkg/layer/encoding/exporter.go
. The current implementation of theEncode
method simply checks for a nil receiver and returns nil, effectively making it a no-op. Meanwhile, encoder implementations still exist in files such asczml.go
,encoder.go
,shp.go
,kml.go
, andgeojson.go
.
- Verify that no external components rely on the previous encoding functionality provided via the
Encoder
field.- Confirm that the no-op
Encode
method does not cause unexpected behavior in layer export functionality.- If the removal is intentional as part of a decoupling refactor, please update documentation and tests accordingly.
server/pkg/asset/id.go (1)
10-26
: LGTM: Well-structured additions for project ID support.The new type alias and variables for ProjectID follow the existing patterns and maintain consistency with the codebase.
server/pkg/nlslayer/feature_test.go (1)
13-17
: LGTM! Test updated to use explicit feature ID creation.The test has been correctly updated to use
NewFeature
with an explicitly created feature ID, which is a better approach as it makes the ID creation more visible and controllable.server/e2e/mock_test.go (1)
11-11
: LGTM! Test setup simplified using helper function.The change improves test maintainability by using
ServerMockTest
helper function instead of explicit server configuration.server/internal/adapter/gql/gqlmodel/convert_asset.go (1)
12-16
: LGTM! Proper handling of optional project ID.The code correctly handles the optional project ID by:
- Using a pointer type for optional field
- Safely checking for nil project before dereferencing
- Creating a new variable to store the ID value before taking its address
server/pkg/asset/asset.go (1)
18-18
: LGTM! Project field added to Asset struct.The optional project field is correctly defined as a pointer type.
server/internal/usecase/interfaces/asset.go (2)
26-31
: LGTM! Well-structured parameter type.The addition of
ProjectID
toCreateAssetParam
is well-typed and maintains backward compatibility by making it optional.
37-44
:✅ Verification successful
Verify implementations of the interface changes.
The interface has undergone significant changes:
- Renamed
FindByWorkspace
toFindByWorkspaceProject
- Added new
Update
method- Renamed
UploadAssetFile
toImportAssetFiles
🏁 Script executed:
#!/bin/bash # Find all implementations of the Asset interface ast-grep --pattern 'type $_ struct { $$$ } func ($_ $_) FindByWorkspaceProject($$$) { $$$ }' # Find any remaining old method names that need updating rg "FindByWorkspace|UploadAssetFile" --type goLength of output: 5035
Asset Interface Changes Verified
The recent modifications to the asset interface have been confirmed:
- The new method signature for
FindByWorkspaceProject
is correctly implemented in both the memory and Mongo adapters.- No references to the outdated method
UploadAssetFile
remain, indicating that the renaming toImportAssetFiles
has been successfully applied.- The introduction of the new
Update
method appears to be integrated as expected.All implementations are consistent with the intended interface changes.
server/pkg/asset/builder.go (1)
57-60
: LGTM! Clean builder method implementation.The new
Project
method follows the builder pattern consistently and maintains the fluent interface design.server/internal/infrastructure/mongo/mongodoc/asset.go (1)
1-1
: Verify PR objectives alignment with code changes.The PR objectives mention integrating Google Analytics parameters into the Story component, but the actual changes implement project association for assets. This suggests a potential mismatch between the PR description and the implemented changes.
Please clarify if this PR's scope has changed or if these changes are prerequisites for the Google Analytics integration.
server/internal/adapter/gql/resolver_mutation_asset.go (1)
19-26
: LGTM! Well-structured project ID handling.The implementation correctly handles the optional project ID with proper error checking and type conversion.
server/pkg/scene/builder/story.go (1)
45-45
: LGTM! Added missing Title field.The change correctly adds the Title field to the JSON output, maintaining consistency with the struct definition.
server/pkg/storytelling/story_bulider.go (1)
124-127
: LGTM! Clean implementation of EnableGa.The method follows the builder pattern correctly and maintains immutability.
server/internal/adapter/gql/loader_asset.go (2)
45-52
: LGTM! Well-implemented project ID handling.The implementation correctly handles the optional project ID with proper error checking and type conversion.
60-66
: LGTM! Performance optimization.Good optimization by pre-allocating the edges array and using index-based assignment instead of append.
server/internal/infrastructure/memory/asset.go (1)
49-59
: LGTM! The project filtering logic is well-implemented.The implementation correctly handles both workspace and project-based filtering, with appropriate null checks for the project ID parameter.
server/e2e/gql_scene_test.go (2)
16-16
: LGTM! Response object access has been standardized.The changes consistently use
Object().Value()
for accessing response properties, improving code clarity and maintainability.Also applies to: 42-42, 71-73
77-100
: LGTM! Well-structured project creation helper.The
createProjectWithExternalImage
function is well-implemented with:
- Clear GraphQL mutation structure
- Comprehensive project parameters
- Proper error handling through the Request helper
server/internal/adapter/gql/gqlmodel/convert_project.go (1)
108-113
: LGTM! Well-structured export type.The
ProjectExport
struct is well-defined with appropriate JSON tags and field types.server/internal/adapter/gql/gqlmodel/convert_storytelling.go (1)
35-36
: LGTM! Google Analytics fields properly integrated.The GA fields are correctly added to the Story struct, aligning with the PR objectives for storytelling GA support.
server/e2e/gql_validate_geojson_test.go (1)
186-186
: LGTM!The addition of the
nil
parameter forprojectId
aligns with the updated function signature in the asset management system.server/internal/usecase/interfaces/story.go (1)
38-39
: LGTM!The addition of
EnableGa
andTrackingID
fields as pointers follows Go best practices for optional fields and aligns with the PR objective to integrate Google Analytics parameters.server/pkg/scene/builder/scene.go (1)
27-27
: LGTM!The simplified function signature improves code clarity by removing unused parameters while maintaining the core scene building functionality.
server/internal/infrastructure/mongo/asset.go (2)
67-67
: LGTM!The function rename and signature update clearly indicate its enhanced capability to filter assets by both workspace and project.
72-80
: LGTM!The filter construction is clear and handles both project-specific and workspace-wide queries appropriately. The conditional logic ensures proper filtering based on the presence of a project ID.
server/internal/app/auth_client.go (1)
105-107
: LGTM! Improved logging by skipping e2e test user.The changes appropriately reduce log noise by skipping logging for the e2e test user while maintaining useful debug information for other users.
Also applies to: 115-117
server/e2e/gql_user_test.go (1)
28-28
:✅ Verification successful
Verify the usage of the new
gateway.File
parameter.The function signature has been updated to include a
gateway.File
parameter, but it's not being used in the function body. Please confirm if this is intentional or if additional implementation is needed.
🏁 Script executed:
#!/bin/bash # Description: Check if the gateway.File parameter is used in other test files. # Search for similar function signatures rg -A 5 'func.*gateway\.File\)' # Search for any usage of gateway.File rg 'gateway\.File'Length of output: 5971
Subject: Confirmed Intentional Omission of
gateway.File
UsageAfter reviewing the repository, it’s clear that the new
gateway.File
parameter is consistently added to seeder function signatures (e.g. inserver/e2e/seeder.go
and defined as part of the commonSeeder
type inserver/e2e/common.go
). In functions likebaseSeederUser
inserver/e2e/gql_user_test.go
, the parameter isn’t utilized in the body, which matches the established pattern across similar functions. This suggests the parameter is provided to conform to the interface requirements and may be reserved for future use rather than an oversight.No changes are needed unless there’s an intention to leverage the parameter within the function body.
server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go (1)
11-13
: LGTM! Consistent refactoring of feature creation.The changes consistently replace
NewFeatureWithNewId
withNewFeature
across all feature creations, maintaining the same functionality while using explicit ID generation.Also applies to: 21-23, 31-33, 41-43, 51-53
server/pkg/storytelling/story.go (3)
41-42
: LGTM! Added Google Analytics fields.The new fields
enableGa
andtrackingID
are appropriately added to support Google Analytics integration.
134-140
: LGTM! Added getter and setter methods for Google Analytics fields.The new methods follow the codebase's naming conventions and provide clean access to the Google Analytics fields.
Also applies to: 190-196
58-60
: LGTM! Added nil check to Pages() method.The added nil check improves robustness by preventing potential nil pointer dereference.
server/internal/infrastructure/mongo/mongodoc/storytelling.go (3)
34-35
: LGTM! Google Analytics fields added.The new fields
EnableGa
andTrackingID
have been correctly added to theStorytellingDocument
struct, aligning with the PR objectives to integrate Google Analytics parameters.
87-88
: LGTM! Google Analytics fields initialized.The new fields are properly initialized in the
NewStorytelling
function by retrieving their values from thestorytelling.Story
object.
197-198
: LGTM! Google Analytics fields included in model.The new fields are correctly included when constructing a
storytelling.Story
object in theModel
method.server/internal/app/app.go (2)
72-78
: LGTM! Authentication handling improved.The changes improve the authentication handling by:
- Reinstating
authConfig
retrieval and logging.- Adding a condition to check if authentication is disabled.
- Using appropriate middleware based on the authentication state.
164-168
:❓ Verification inconclusive
Verify the usage of dummy authentication.
The
AuthMiddlewareDummy
function bypasses authentication by simply passing through to the next handler. While this is useful for testing or development environments, ensure it's not used in production.Run the following script to verify the usage of disabled authentication:
🏁 Script executed:
#!/bin/bash # Description: Check for potential misuse of disabled authentication in production code. # Test: Search for disabled authentication configuration. Expect: Only in test files. rg -A 5 $'AuthSrv.Disabled = true' # Test: Search for dummy auth usage. Expect: Only in test files. rg -A 5 $'AuthMiddlewareDummy'Length of output: 690
Critical: Confirm Environment-Specific Use of Dummy Authentication
The dummy authentication middleware (
AuthMiddlewareDummy
) bypasses all auth checks by simply passing control to the next handler. It is invoked inserver/internal/app/app.go
(see the assignment towrapHandler
). Please ensure that:
- This dummy middleware is strictly gated by configuration (e.g., via an
AuthSrv.Disabled
flag or its equivalent) so that it is only enabled in development or testing scenarios.- A thorough review of the conditional code paths confirms that production deployments will always enable proper authentication.
server/internal/infrastructure/fs/file.go (2)
113-113
: LGTM! Asset path validation improved.The path validation in
RemoveAsset
now correctly includesassetDir
in the expected directory path, ensuring assets are only removed from their designated directory.
286-286
: LGTM! Asset URL structure improved.The URL construction in
getAssetFileURL
now correctly includesassetDir
in the path, ensuring a consistent URL structure for assets.web/src/services/api/propertyApi/utils.ts (1)
247-248
:✅ Verification successful
Verify the removal of
actualValue
field.The
actualValue
field has been removed from theMergedPropertyField
type. While this simplifies the data structure, ensure that all consumers of this type have been updated accordingly.Run the following script to verify the usage of
actualValue
:
🏁 Script executed:
#!/bin/bash # Description: Check for any remaining references to actualValue. # Test: Search for actualValue usage. Expect: No references in property-related code. rg -A 5 $'actualValue' # Test: Search for MergedPropertyField type usage. Expect: Updated type definitions. ast-grep --pattern $'type MergedPropertyField = { $$$ actualValue $$$ }'Length of output: 13944
Confirmed:
actualValue
field successfully removed fromMergedPropertyField
The updated code in
web/src/services/api/propertyApi/utils.ts
now shows:// "fieldId" | "actualValue" | "overridden" "fieldId" | "overridden"which indicates that the
actualValue
field has been removed as intended.Verification via the provided commands shows no remaining references to
actualValue
within the type definition. While some unrelated files (e.g., tests and GraphQL fragments) still reference the identifier (or have it commented), these do not affect consumers of theMergedPropertyField
type.Please ensure that any external consumers have been updated accordingly.
server/internal/infrastructure/fs/file_test.go (3)
116-117
: LGTM! Base URL change maintains test functionality.The change from
/assets
to/
as the base URL is correct, as the test still verifies that asset paths are properly constructed with/assets/
prefix (see assertion on line 128).
165-165
: LGTM! Base URL change maintains test functionality.The change from
https://example.com/assets
tohttps://example.com/
is correct, as the test cases still verify proper asset path handling with/assets/
prefix in URLs.
316-316
: LGTM! Base URL change maintains test functionality.The change from
http://hoge.com/assets
tohttp://hoge.com/
is correct, as the test still verifies proper asset URL construction.server/internal/adapter/gql/resolver_mutation_storytelling.go (1)
58-59
: LGTM! Google Analytics parameters properly integrated.The addition of
EnableGa
andTrackingID
fields to theUpdateStoryInput
struct correctly implements the PR objective of adding Google Analytics support to stories.server/e2e/common.go (2)
35-43
: LGTM! Well-structured configuration variables.The addition of
fr
anddisabledAuthConfig
variables improves code organization by centralizing configuration.
283-297
: LGTM! Improved type handling in ValueDump.The update to
ValueDump
properly handles different types and includes helpful error messaging.server/internal/usecase/interactor/project.go (1)
513-513
: LGTM! Improved asset handling in export functionality.The renaming to
ExportProjectData
and use ofAddZipAsset
helper function improves code organization and reusability.Also applies to: 526-527
server/e2e/gql_custom_property_test.go (1)
347-762
: LGTM! Well-structured GraphQL query with improved organization.The query has been enhanced with:
- Better organization using fragments for improved readability and maintainability
- Comprehensive coverage of all necessary fields including GA support
- Logical grouping of related fields
server/e2e/gql_project_test.go (1)
42-42
: LGTM! Test assertions updated to match expected project counts.The test expectations have been correctly updated to reflect the current project count requirements.
Also applies to: 144-144, 585-585, 617-617
server/internal/usecase/interactor/scene.go (2)
680-683
: LGTM! Improved null safety in story handling.The code now safely handles the case where the story list is nil or empty, preventing potential nil pointer dereferences.
711-714
: LGTM! Enhanced asset handling with better error logging.The code now uses
AddZipAsset
with improved error handling and logging.server/internal/usecase/interactor/storytelling.go (1)
188-194
: LGTM! Added Google Analytics support to story updates.The implementation correctly handles the new GA fields:
EnableGa
for enabling/disabling GATrackingID
for setting the GA tracking IDserver/internal/usecase/interactor/nlslayer.go (1)
859-861
: LGTM! Feature creation logic has been improved.The change to use
nlslayer.NewFeatureID()
for generating feature IDs is a good practice as it ensures consistent ID generation.server/gql/scene.graphql (1)
50-50
: LGTM! Improved GraphQL schema formatting.The added space after
extend type Query
improves readability and follows GraphQL schema formatting conventions.server/gql/asset.graphql (1)
29-32
: LGTM! Well-structured update types.The new
UpdateAssetInput
andUpdateAssetPayload
types are well-defined and maintain consistency with GraphQL best practices.Also applies to: 49-52
server/gql/plugin.graphql (1)
99-99
: LGTM! Improved GraphQL schema formatting.The formatting changes improve readability while maintaining the schema's functionality.
Also applies to: 109-109
server/gql/workspace.graphql (2)
8-14
: LGTM! The addition of projectId parameter enhances asset filtering capabilities.The new parameter allows for more granular asset queries within specific projects.
15-21
: LGTM! Code formatting improvements enhance readability.The multi-line formatting of method signatures and mutation parameters improves code readability while maintaining functionality.
Also applies to: 121-123
server/gql/storytelling.graphql (1)
75-89
: LGTM! Well-organized input type with clear section comments.The Google Analytics fields are properly added to the UpdateStoryInput type, maintaining consistency with other optional update fields. The section comments improve code organization.
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 (7)
server/gql/scene.graphql (1)
1-17
: 💡 Verification agent🧩 Analysis chain
Consider updating the schema to support GA in stories.
The PR objectives mention adding Google Analytics support to the Story component with
enableGa
andtrackingId
fields. Since the Scene type includes astories
field, consider updating the Story type schema to include these new GA-related fields.Let's check if the Story type schema exists and needs to be updated:
🏁 Script executed:
#!/bin/bash # Description: Check for Story type schema definition rg -A 10 'type Story' server/gql/Length of output: 2761
Update Story Schema for GA Support
The Story type defined in server/gql/storytelling.graphql does not yet include the GA-related fields described in the PR objectives. To fully support Google Analytics in stories, please update the Story schema by adding the new fields—likely
enableGa
(Boolean) andtrackingId
(String).server/pkg/scene/builder/story.go (2)
11-18
:⚠️ Potential issueAdd missing Google Analytics fields to
storyJSON
struct.According to the PR objectives, the Story component should include Google Analytics support. Please add the following mandatory fields to the struct:
EnableGa bool
- For enabling/disabling Google AnalyticsTrackingId string
- For the Google Analytics tracking IDApply this diff to add the missing fields:
type storyJSON struct { ID string `json:"id"` Title string `json:"title"` Property propertyJSON `json:"property"` Pages []pageJSON `json:"pages"` PanelPosition string `json:"position"` BgColor string `json:"bgColor"` + EnableGa bool `json:"enableGa"` + TrackingId string `json:"trackingId"` }
38-56
:⚠️ Potential issueUpdate
storyJSON
function to support Google Analytics.The function needs to be updated to accept and populate the Google Analytics fields.
Apply this diff to update the function:
-func (b *Builder) storyJSON(ctx context.Context, p []*property.Property) (*storyJSON, error) { +func (b *Builder) storyJSON(ctx context.Context, p []*property.Property, enableGa bool, trackingId string) (*storyJSON, error) { if b.story == nil { return nil, nil } return &storyJSON{ ID: b.story.Id().String(), Title: b.story.Title(), Property: b.property(ctx, findProperty(p, b.story.Property())), Pages: lo.FilterMap(b.story.Pages().Pages(), func(page *storytelling.Page, _ int) (pageJSON, bool) { if page == nil { return pageJSON{}, false } return b.pageJSON(ctx, *page, p), true }), PanelPosition: string(b.story.PanelPosition()), BgColor: b.story.BgColor(), + EnableGa: enableGa, + TrackingId: trackingId, }, nil }server/pkg/scene/builder/scene.go (1)
23-24
: 🛠️ Refactor suggestionFix inconsistent field names and JSON tags.
The Google Analytics field names and JSON tags are inconsistent with the naming convention used in
storyJSON
:
EnableGA
vsEnableGa
TrackingID
vsTrackingId
enableGa
vsenableGA
trackingId
vstrackingID
Apply this diff to maintain consistency:
- EnableGA bool `json:"enableGa"` - TrackingID string `json:"trackingId"` + EnableGa bool `json:"enableGa"` + TrackingId string `json:"trackingId"`server/pkg/layer/encoding/exporter.go (1)
37-42
:⚠️ Potential issueCritical: Encode method now returns nil without performing any encoding.
The
Encode
method has been modified to returnnil
without performing any encoding operations. This could silently break functionality that depends on proper layer encoding.Please either:
- Restore the encoding functionality
- Document why encoding is no longer needed
- Update all dependent code to handle this change
server/internal/usecase/repo/asset.go (1)
18-26
: 💡 Verification agent🧩 Analysis chain
LGTM! Well-structured interface change to support project-based filtering.
The addition of
FindByWorkspaceProject
with project ID support is a good enhancement to the asset management system.
🏁 Script executed:
#!/bin/bash # Verify that all Asset implementations have been updated rg -A 5 "type\s+.*Asset\s+struct" # Search for any remaining references to FindByWorkspace rg "FindByWorkspace\("Length of output: 5522
Action Required: Update All Call Sites to Use
FindByWorkspaceProject
The interface change in server/internal/usecase/repo/asset.go is well-structured and enhances asset filtering by introducing project support. However, the verification revealed several remaining references to the old
FindByWorkspace
method. Please review and update these call sites to ensure consistency with the new interface method. For example:
GraphQL Adapters:
server/internal/adapter/gql/loader_asset.go
still defines aFindByWorkspace
method.server/internal/adapter/gql/loader_project.go
and resolver files (e.g.,resolver_query.go
,resolver_team.go
) are invokingFindByWorkspace
.Usecase and Repository Layers:
- References in
server/internal/usecase/interactor/project.go
andserver/internal/usecase/repo/project.go
still use the old method signature.Please verify that all implementations of the Asset interface and their consumers are updated to use
FindByWorkspaceProject
to avoid potential integration issues.server/go.mod (1)
192-192
: 💡 Verification agent🧩 Analysis chain
Verify Go version.
The specified Go version
1.23.5
appears to be incorrect as it's higher than the latest available Go version.
🌐 Web query:
What is the latest version of Go programming language?
💡 Result:
The latest stable release of the Go programming language as of February 2025 is Go 1.23.6, released on February 4, 2025[2][6]. This follows six months after Go 1.22 and continues Go's tradition of maintaining backward compatibility while introducing toolchain and runtime improvements[3][4].
Key details about current versions:
- Latest minor update:
1.23.6
contains security fixes for crypto/elliptic and bug fixes to the compiler/go command[2]- Previous major versions still receiving security updates:
1.22.x
line up to v1.22.12[2]1.21.x
line up to v1.21.12[2]Go uses a unique versioning scheme where:
- The second number indicates major releases (e.g.,
1.**23**.0
)- The third number represents patch/security updates (e.g.,
1.23.**6**
)- Maintains compatibility across all
1.x
versions per the Go 1 promise[5]The language currently uses time-based releases every 6 months, with new major versions typically arriving in August and February each year[3][4]. Developers are encouraged to update regularly while older versions remain supported for security fixes through at least two subsequent major releases[5].
Citations:
- 1: http://forum.exercism.org/t/update-go-version-to-the-latest-one/9326
- 2: https://go.dev/doc/devel/release
- 3: https://tip.golang.org/doc/go1.23
- 4: https://tip.golang.org/doc/go1.22
- 5: https://en.wikipedia.org/wiki/Go_(programming_language)
- 6: https://go.dev/dl
- 7: https://groups.google.com/g/golang-nuts/c/U99js3UYz-U
- 8: https://github.com/golang/go
Consider updating the Go version in go.mod.
The file currently specifies:go 1.23.5As of February 2025, the latest stable release is Go 1.23.6—which includes important security fixes for crypto/elliptic and bug fixes for the compiler and go command. While using 1.23.5 is acceptable if intentional, please verify whether updating to 1.23.6 is desired to ensure you benefit from the most recent improvements.
🧹 Nitpick comments (29)
web/src/services/api/propertyApi/utils.ts (1)
247-248
: Remove commented-out type signature.The type signature change looks good, but please remove the commented-out old type signature as it's not providing value.
- // "fieldId" | "actualValue" | "overridden" "fieldId" | "overridden"
server/pkg/scene/builder/builder.go (1)
135-135
: Improve consistency of error messages.The error messages in
BuildResult
could be more consistent. Consider standardizing the format:- return nil, errors.New("Fail buildScene :" + err.Error()) - return nil, errors.New("Fail buildStory :" + err.Error()) - return nil, errors.New("Fail buildNLSLayers :" + err.Error()) - return nil, errors.New("Fail buildLayerStyles :" + err.Error()) + return nil, fmt.Errorf("failed to build scene: %w", err) + return nil, fmt.Errorf("failed to build story: %w", err) + return nil, fmt.Errorf("failed to build NLS layers: %w", err) + return nil, fmt.Errorf("failed to build layer styles: %w", err)This change:
- Uses consistent capitalization
- Uses
fmt.Errorf
with%w
for proper error wrapping- Improves readability with proper spacing
Also applies to: 141-141, 149-149, 157-157
server/internal/usecase/interactor/asset.go (1)
108-127
: Proper transactional update for asset project.The
Update
method correctly applies a transaction when modifying an asset’s project association. Ensure that relevant validations (e.g., workspace ownership checks) remain up to date with potential future changes.server/internal/adapter/gql/resolver_mutation_project.go (4)
183-199
: Filter or sanitize parsed asset names before using them.After retrieving asset URLs, the code extracts file names via
path.Base()
. Consider validating or sanitizing these names if derived from user-generated data. This helps avoid complications if the file name contains unexpected characters or malicious patterns.
247-269
: Check for large memory usage when calling bytes.Replace repeatedly.When iterating over multiple assets, repeated calls to
bytes.Replace
can become expensive in memory usage. If this operation scales significantly (e.g., thousands of assets), consider streaming or more incremental replacements.
346-354
: Potential advanced validation for project data.
unmarshalProject
simply performs JSON unmarshalling and returns a map. Depending on the complexity of your project data, you might consider more robust schema-based validation or typed structures for future maintainability.
356-370
: Ensure concurrency-safety if unmarshal logic is reused widely.
unmarshalAssets
is straightforward. If multiple goroutines eventually process the same data, confirm that no race conditions exist modifying the returned map.server/e2e/gql_project_export_import_test.go (2)
21-63
: Consider renaming the helper function for clarity.
The function call at line 29 referencesexporProject
, which appears to be a misspelling of "exportProject."- fileName := exporProject(t, e, pID.String()) + fileName := exportProject(t, e, pID.String())
58-61
: Improve error handling when removing temporary files.
Although the error fromos.Remove
is asserted viaassert.Nil(t, err)
, consider usingassert.NoError(t, err)
or providing a more descriptive message to clarify any failures.server/e2e/seeder.go (2)
125-167
: Check file path safety and error handling.
While opening and uploading the file, ensure that potential directory traversal or permission issues are addressed. A sanitization or path validation step could prevent unexpected usage.
283-312
: Consider adding more test coverage for new storytelling data.
TheaddStory
function seeds a single test page. Confirm if additional scenarios (e.g., multiple pages, blocks) should also be tested to reflect real usage.server/internal/usecase/interactor/scene.go (2)
711-715
: Add error handling or fallback logic for malformed URLs.
WhenurlStr
is parsed at line 711, errors are silently ignored (_ = url.Parse(urlStr)
). Consider logging or returning an error if the URL cannot be parsed.
765-771
: Confirm allowed file types for block source.
Similar to widget icons, ensure the data infield.Value()
matches acceptable file types, rather than allowing arbitrary external references.server/internal/adapter/gql/gqlmodel/models_gen.go (1)
581-589
: Consider typed structures instead ofany
Using
any
grants flexibility but loosens type safety. If possible, consider a more specific or typed approach to reduce potential runtime errors and improve clarity.server/internal/infrastructure/mongo/mongodoc/asset.go (1)
17-17
: LGTM! Consider adding a comment for the Project field.The Project field is well-placed in the struct. Consider adding a comment similar to the Team field to document its purpose.
- Project *string + Project *string // Project ID associated with the assetserver/internal/adapter/gql/resolver_mutation_asset.go (2)
19-26
: LGTM! Consider consolidating error variable names.The project ID handling is well-implemented. Consider using consistent error variable names across the file.
- if err != nil { + if err2 := err; err2 != nil {
41-62
: LGTM! Clean implementation of UpdateAsset mutation.The UpdateAsset mutation is well-structured with proper error handling. Consider adding a comment describing the mutation's purpose.
+// UpdateAsset updates an asset's project association func (r *mutationResolver) UpdateAsset(ctx context.Context, input gqlmodel.UpdateAssetInput) (*gqlmodel.UpdateAssetPayload, error) {
server/internal/adapter/gql/gqlmodel/convert_project.go (1)
127-137
: Consider adding error details in JSON conversion.The function silently returns nil on JSON conversion errors, which might make debugging harder.
Consider returning an error or logging the conversion failures:
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { var p ProjectExport bytes, err := json.MarshalIndent(data, "", " ") if err != nil { + log.Printf("Failed to marshal project export data: %v", err) return nil } if err := json.Unmarshal(bytes, &p); err != nil { + log.Printf("Failed to unmarshal project export data: %v", err) return nil } return &p }server/internal/infrastructure/mongo/asset.go (2)
72-74
: Consider using a builder pattern for filter construction.The current filter construction with direct assignments could be made more maintainable using a builder pattern or helper function, especially as more conditions are added.
Also applies to: 76-80
87-94
: Improve error handling for bucket pattern.The bucket pattern determination logic could benefit from better error handling and clearer conditions.
Consider extracting this logic into a separate function with proper error handling:
+func determineBucketPattern(ctx context.Context) string { + pattern := adapter.CurrentHost(ctx) + switch { + case pattern == "": + return "example.com" // e2e test + case strings.Contains(pattern, "localhost"): + return "localhost" + default: + return "visualizer" + } +}server/internal/app/app.go (1)
164-168
: Consider adding documentation for the dummy auth middleware.While the implementation is correct, adding documentation would help explain the purpose and use case of this middleware.
Add documentation above the function:
+// AuthMiddlewareDummy returns a middleware that passes requests through without authentication. +// This is used when authentication service is disabled. func AuthMiddlewareDummy() (func(http.Handler) http.Handler, error) { return func(next http.Handler) http.Handler { return next }, nil }server/e2e/common.go (4)
35-43
: Consider using a more descriptive variable name thanfr
.The global variable
fr
is not self-documenting. A more descriptive name likeglobalFileRef
would better convey its purpose.-var ( - fr *gateway.File - disabledAuthConfig = &config.Config{ - Origins: []string{"https://example.com"}, - AuthSrv: config.AuthSrvConfig{ - Disabled: true, - }, - } -) +var ( + globalFileRef *gateway.File + disabledAuthConfig = &config.Config{ + Origins: []string{"https://example.com"}, + AuthSrv: config.AuthSrvConfig{ + Disabled: true, + }, + } -)
51-71
: Consider using a more idiomatic error handling pattern.The function could benefit from a more idiomatic error handling pattern by returning the error first.
-func initRepos(t *testing.T, useMongo bool, seeder Seeder) (repos *repo.Container, file gateway.File) { +func initRepos(t *testing.T, useMongo bool, seeder Seeder) (*repo.Container, gateway.File, error) { ctx := context.Background() if useMongo { db := mongotest.Connect(t)(t) accountRepos := lo.Must(accountmongo.New(ctx, db.Client(), db.Name(), false, false, nil)) - repos = lo.Must(mongo.New(ctx, db, accountRepos, false)) + repos, err := mongo.New(ctx, db, accountRepos, false) + if err != nil { + return nil, nil, fmt.Errorf("failed to create mongo repo: %w", err) + } } else { repos = memory.New() } - file = lo.Must(fs.NewFile(afero.NewMemMapFs(), "https://example.com/")) - fr = &file + file, err := fs.NewFile(afero.NewMemMapFs(), "https://example.com/") + if err != nil { + return nil, nil, fmt.Errorf("failed to create file: %w", err) + } + globalFileRef = &file if seeder != nil { if err := seeder(ctx, repos, file); err != nil { - t.Fatalf("failed to seed the db: %s", err) + return nil, nil, fmt.Errorf("failed to seed the db: %w", err) } } - return repos, file + return repos, file, nil }
73-82
: Consider initializing file in a separate function.The file initialization logic could be extracted into a separate function for better reusability and testability.
+func initFile() (gateway.File, error) { + return fs.NewFile(afero.NewMemMapFs(), "https://example.com/") +} func initGateway() *gateway.Container { if fr == nil { + file, err := initFile() + if err != nil { + return &gateway.Container{} + } return &gateway.Container{ - File: lo.Must(fs.NewFile(afero.NewMemMapFs(), "https://example.com/")), + File: file, } } return &gateway.Container{ File: *fr, } }
627-649
: Consider adding error documentation and handling external URLs.The
AddZipAsset
function could benefit from better documentation and explicit handling of external URLs.+// AddZipAsset adds an asset to a zip file. If the asset path is an external URL, +// the function silently skips it and returns nil. +// Returns an error if reading the asset or writing to the zip file fails. func AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error { fileName := strings.TrimPrefix(path, "/assets/") + + // Skip if the path is an external URL + if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { + return nil + } + stream, err := file.ReadAsset(ctx, fileName) if err != nil { return nil // skip if external URL } defer func() { if cerr := stream.Close(); cerr != nil { - fmt.Printf("Error closing file: %v\n", cerr) + // Log the error but don't fail the operation + fmt.Printf("Warning: error closing file %s: %v\n", fileName, cerr) } }() zipEntryPath := fmt.Sprintf("assets/%s", fileName) zipEntry, err := zipWriter.Create(zipEntryPath) if err != nil { - return err + return fmt.Errorf("failed to create zip entry for %s: %w", fileName, err) } _, err = io.Copy(zipEntry, stream) if err != nil { _ = stream.Close() - return err + return fmt.Errorf("failed to copy asset %s to zip: %w", fileName, err) } return nil }server/internal/usecase/interactor/project.go (1)
513-533
: Consider adding context timeout for export operations.Long-running export operations should have a timeout to prevent resource exhaustion.
func (i *Project) ExportProjectData(ctx context.Context, projectID id.ProjectID, zipWriter *zip.Writer, operator *usecase.Operator) (*project.Project, error) { + // Add timeout for export operation + ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() prj, err := i.projectRepo.FindByID(ctx, projectID) if err != nil { return nil, errors.New("project " + err.Error()) } if prj.IsDeleted() { - fmt.Printf("Error Deleted project: %v\n", prj.ID()) + return nil, fmt.Errorf("project %s is deleted", prj.ID()) }server/e2e/gql_custom_property_test.go (1)
347-762
: Consider extracting GraphQL fragments into a separate file.The GraphQL query has grown significantly with multiple fragments. Consider moving these fragments to a separate file to improve maintainability and reusability.
Example structure:
server/ e2e/ fragments/ property.graphql widget.graphql story.graphql queries/ scene.graphql
This would make the test file more focused and the fragments reusable across different tests.
server/Makefile (1)
85-90
: Consider a more descriptive target name and add safety checks.The
run-reset
target performs destructive operations. Consider:
- Renaming to
reset-mongo-data
for clarity- Adding safety checks before data removal
Apply this diff to improve the target:
-run-reset: +reset-mongo-data: + @echo "WARNING: This will delete all MongoDB data. Are you sure? (y/n)" && read ans && [ $$ans = y ] docker stop reearth-visualizer-reearth-mongo-1 rm -rf ../mongo rm -rf data make run-db make mockuserserver/e2e/gql_storytelling_test.go (1)
231-413
: Publication workflow validation
This test rigorously checks publishing a story, including reading back the published JSON data via file storage and HTTP endpoint. The scenario is comprehensive and confirms the final structure of the published story, including default GA settings. Consider adding a variation for GA enabled to ensure coverage of all states.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
server/e2e/test.zip
is excluded by!**/*.zip
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 (107)
server/Makefile
(3 hunks)server/e2e/common.go
(7 hunks)server/e2e/dataset_export_test.go
(0 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_custom_property_test.go
(1 hunks)server/e2e/gql_layer_test.go
(0 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_nlslayer_test.go
(0 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 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/storytelling.graphql
(3 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_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_plugin.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_scene.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 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
(9 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
(3 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 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/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/layer.go
(0 hunks)server/internal/usecase/interactor/nlslayer.go
(3 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/scene.go
(5 hunks)server/internal/usecase/interactor/scene_test.go
(0 hunks)server/internal/usecase/interactor/storytelling.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 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/nlslayer/feature.go
(0 hunks)server/pkg/nlslayer/feature_test.go
(1 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)server/pkg/scene/builder/story.go
(1 hunks)server/pkg/scene/builder_test.go
(0 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)web/src/services/api/propertyApi/utils.ts
(1 hunks)
⛔ Files not processed due to max files limit (6)
- web/src/services/api/sceneApi.ts
- web/src/services/gql/fragments/dataset.ts
- 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/scene.ts
💤 Files with no reviewable changes (47)
- server/internal/adapter/gql/gqlmodel/convert.go
- server/pkg/scene/builder_test.go
- server/pkg/layer/layerops/processor_test.go
- server/internal/adapter/gql/gqlmodel/convert_plugin.go
- server/internal/adapter/gql/gqldataloader/dataloader.go
- server/e2e/gql_nlslayer_test.go
- server/internal/adapter/gql/context.go
- server/internal/usecase/interactor/project_test.go
- server/pkg/nlslayer/feature.go
- server/pkg/scene/builder/encoder_test.go
- server/pkg/scene/builder/builder_test.go
- server/internal/adapter/gql/gqlmodel/convert_scene.go
- server/internal/adapter/gql/resolver_property_test.go
- server/e2e/dataset_export_test.go
- server/internal/usecase/interactor/scene_test.go
- server/internal/adapter/gql/gqlmodel/models.go
- server/gql/property.graphql
- server/internal/adapter/gql/gqlmodel/convert_dataset.go
- server/e2e/gql_project_export_test.go
- server/internal/adapter/gql/resolver_mutation_scene.go
- server/gql/cluster.graphql
- server/internal/infrastructure/mongo/mongodoc/scene.go
- server/internal/adapter/gql/resolver_layer.go
- server/gqlgen.yml
- server/gql/dataset.graphql
- server/internal/usecase/interactor/layer.go
- server/internal/adapter/gql/resolver_property.go
- server/internal/adapter/gql/resolver_scene.go
- server/e2e/gql_layer_test.go
- server/internal/adapter/gql/loader.go
- server/internal/adapter/gql/gqlmodel/convert_tag.go
- server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
- server/internal/adapter/gql/gqlmodel/convert_layer.go
- server/internal/adapter/gql/resolver_mutation_dataset.go
- server/gql/layer.graphql
- server/internal/adapter/gql/resolver_dataset.go
- server/gql/tag.graphql
- server/internal/adapter/gql/resolver_mutation_tag.go
- server/internal/adapter/gql/resolver_dataset_schema.go
- server/internal/adapter/gql/resolver_tag.go
- server/internal/adapter/gql/gqldataloader/tagloader_gen.go
- server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
- server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
- server/internal/adapter/gql/resolver_mutation_layer.go
- server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
- server/internal/adapter/gql/loader_dataset.go
- server/internal/adapter/gql/loader_tag.go
✅ Files skipped from review due to trivial changes (1)
- server/pkg/scene/builder/encoder.go
🧰 Additional context used
🧠 Learnings (2)
server/internal/adapter/gql/resolver_mutation_project.go (2)
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`.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
🔇 Additional comments (96)
server/internal/adapter/gql/resolver_team.go (1)
27-34
: LGTM! Method signature and implementation updated correctly for project-based asset filtering.The changes properly integrate project-based asset filtering by:
- Adding the optional
projectID
parameter to the method signature- Correctly passing it to the asset loader's
FindByWorkspace
methodHowever, I notice a discrepancy between these changes and the PR objectives. The PR description mentions adding Google Analytics support to the Story component, but these changes are about project-based asset operations.
Likely an incorrect or invalid review comment.
server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go (1)
11-13
: LGTM! Improved API design with explicit ID generation.The consistent replacement of
NewFeatureWithNewId
with explicit ID generation usingNewFeature(nlslayer.NewFeatureID(), ...)
improves code clarity and provides better control over feature creation. This change aligns well with the principle of explicit over implicit behavior.Also applies to: 21-23, 31-33, 41-43, 51-53
web/src/services/api/propertyApi/utils.ts (1)
247-248
: Verify if these changes belong to this PR.These changes appear unrelated to the PR's stated objective of adding Google Analytics support to the Story component. Please verify if these changes should be part of this PR or moved to a separate one.
server/gql/scene.graphql (1)
50-50
: LGTM!The formatting change improves readability by adding a space after the Query type extension declaration.
server/pkg/scene/builder/scene.go (1)
27-40
: LGTM!The function correctly accepts and populates the Google Analytics fields.
server/internal/adapter/gql/gqlmodel/convert_asset.go (3)
12-16
: LGTM! Well-implemented project ID handling.The code safely handles the optional project ID using proper nil checks and pointer semantics.
22-22
: LGTM! Correct ProjectID field update.The ProjectID field is properly updated to use the new pid variable, maintaining consistency with the rest of the struct initialization.
1-63
:❓ Verification inconclusive
Verify PR objectives alignment.
The changes in this file implement project ID support for assets, which appears to be unrelated to the stated PR objective of adding Google Analytics support to the Story component. Please clarify if this is an unintended inclusion or if the PR description needs to be updated.
Let's verify if there are any Story-related or Google Analytics changes that should be associated with this PR:
🏁 Script executed:
#!/bin/bash # Search for Story or Google Analytics related changes echo "Searching for Story-related changes..." rg -l "Story|GoogleAnalytics|enableGa|trackingId" echo "Checking GraphQL schema changes..." fd -g "*.graphql" --exec cat {}Length of output: 45070
Clarify PR Objectives: Verify Inclusion of Asset Project ID Changes
The conversion functions inserver/internal/adapter/gql/gqlmodel/convert_asset.go
now include support for project IDs on assets. However, the PR description indicates that the changes target adding Google Analytics support for the Story component. Please clarify whether the asset project ID changes are intentionally part of this PR or if the PR description should be updated to reflect multiple objectives.server/pkg/scene/builder/builder.go (2)
91-91
: LGTM: Google Analytics parameters properly integrated.The addition of
enableGa
andtrackingId
parameters to the builder methods is well-structured and consistently propagated through the call chain.Also applies to: 128-128, 165-165
176-179
:❓ Verification inconclusive
Verify layer export functionality after encoder removal.
The removal of the
encoder
field andlayers
parameter suggests a significant architectural change. While the layer export functionality is maintained throughexporter.ExportLayerByID
, we should verify that all layer data is still properly processed.Also applies to: 181-181
🏁 Script executed:
#!/bin/bash # Search for any remaining references to the encoder or layer processing rg -A 5 'encoder|layers' server/pkg/scene/Length of output: 4771
Verify layer export functionality remains intact post-refactor.
The removal of the
encoder
field from theBuilder
struct and the associatedlayers
parameter in thesceneJSON
call has been confirmed. Inserver/pkg/scene/builder/builder.go
(lines 176–179), the export of layers now directly callsb.exporter.ExportLayerByID(ctx, b.scene.RootLayer())
. Additionally, our code search shows that layer processing is still referenced in other modules (e.g.,nlsLayer.go
,dataset_migrator.go
, andplugin_migrator.go
).Please ensure that your existing tests or additional manual validations confirm that all layer data is exported correctly despite these architectural changes.
server/internal/usecase/interactor/asset.go (3)
39-44
: Ensure consistent query for project-level asset retrieval.The new
FindByWorkspaceProject
method now filters assets by both workspace and project. Confirm that any higher-level logic invoking this method is aligned with these parameters to prevent unintended results.
91-91
: Good addition of project association during asset creation.Adding
.Project(inp.ProjectID)
during asset building allows for clear alignment of new assets to a specific project. No further issues spotted.
154-182
: Validate file import logic for large or unusual file names.In
ImportAssetFiles
, setting the file path torealName
and then deriving a new name viapath.Base(realName)
is logically sound. However, certain edge cases (extremely long file names or path injection attempts) might require additional sanitization or validation checks.server/e2e/gql_asset_test.go (3)
15-119
: Inconsistent test coverage for coreSupport flags.Within
TestCoreSupportGetAssets
, multiple assets are created (some withcoreSupport=true
, some withcoreSupport=false
), but the final check at lines 115–118 forcescoreSupport
to betrue
for all returned assets. This seems inconsistent. Confirm whether the test environment or the logic is intended to return only assets withcoreSupport=true
, or whether additional test validations forcoreSupport=false
are needed.
122-179
: Good approach for testing project-based asset association.
TestAssociateProjectGetAssets
thoroughly covers the workflow of creating assets under different projects, reassigning them, and verifying retrieval. It effectively demonstrates correct usage ofprojectId
. Ensure edge cases (like changingprojectId
from one project to another multiple times) are also considered if relevant for your application.
181-350
: GraphQL mutation structures and helper functions look consistent.The additions to the GraphQL schema and supporting test functions (
updateAsset
,removeAsset
,getAssets
) properly handleprojectId
and confirm the new workflow for associating assets with projects. No immediate issues found; the code is cohesive and logically correct.server/internal/adapter/gql/resolver_mutation_project.go (4)
130-155
: Perform additional cleanup and checks in export process.Here, you create a ZIP file named by the lowercased project ID, then remove the file locally after uploading. Verify that error handling logic covers partial uploads or I/O errors that occur mid-transfer.
225-233
: Transaction handling is correct, but carefully finalize error propagation.By deferring
tx.End(ctx)
, you appropriately finalize or roll back changes. Validate that any mid-operation error sets the correct state, ensuring no partial writes remain.
335-344
: Well-structured scene ID replacement.
replaceOldSceneID
cleanly identifies and replaces the scene ID in the exported JSON data. This approach is concise and meets typical needs.
372-382
: Adequate breakdown of plugins and scene data.
unmarshalPluginsScene
nicely separates plugins, scenes, and schemas. Keep an eye on error handling and typed conversions to avoid unexpected panics in case of invalid input.server/e2e/gql_project_export_import_test.go (1)
71-101
: Ensure robust handling of unexpected shape mismatches.
ThecompareValue
function logs a failure if lines differ but continues matching other lines. Double-check whether the test should abort early or continue checking all lines for better debugging.server/e2e/seeder.go (3)
58-71
: Validate presence of workspace ID before base setup.
Currently,baseSeeder
callsbaseSetup
assuming the workspace from the user has been properly established. If a new workspace were required, ensure a prior check or creation logic is in place.
395-448
: Validate schema version when creating layers.
InaddLayerSimple
, a JSON schema is unmarshaled. Confirm that this schema is compatible with current and future versions if there are multiple schema definitions.
512-527
: Double-check UUID generation for compliance.
ThegenerateUUID
method uses a standard approach. Ensure no collisions occur in concurrent seeds or if any external constraints exist on allowable ID formats.server/internal/usecase/interactor/scene.go (3)
22-22
: GA integration aligns with PR objectives.
The import of thestorytelling
package and usage ofEmitGA
references (prj.EnableGA()
,prj.TrackingID()
) conform to the stated PR objective of enabling Google Analytics within stories.
680-683
: Ensure correct selection of multiple stories.
Here, only the first story is selected if multiple exist. If multi-story usage is expected, consider handling all stories or clarifying that only the first is relevant.
740-745
: Validate button icon URLs beyond presence.
A valid URL is assumed. If the URL is invalid or points to an inaccessible path,AddZipAsset
might fail. Add robust checks or logs for debugging.server/internal/adapter/gql/gqlmodel/models_gen.go (7)
138-138
: Verify usage ofProjectID
The new optional
ProjectID
field on theAsset
type looks good, but please ensure that wherever it's set, the user has the necessary permissions, and that it's validated to avoid potential orphan references.
905-916
: Reuse previous feedback aboutany
usageSame concern applies here as in the
MergedPropertyField
. If feasible, reduce usage ofany
to make the schema more robust and discoverable.
919-922
: Looks good: clarified dataset linkageThese optional fields enhance clarity around linking property fields to dataset schemas. No further issues found.
1135-1149
: Double-check references to newly added fields inScene
The new arrays for
Stories
,Styles
, and related references look consistent. Verify that these additions are properly tested for scenarios such as empty arrays or permission checks.
1221-1222
: GA fields aligned with PR objectivesAdding
EnableGa
andTrackingID
directly to theStory
type as mandatory fields matches the PR requirements for GA support.
1332-1340
: Validate newprojectId
references in asset updatesEnsure that the resolvers or services handling
UpdateAssetInput
and its payload account for the newly introducedprojectId
field and that it’s guarded by applicable access checks.
1459-1460
: Optional GA parameters well-definedThe optional
enableGa
andtrackingId
fields inUpdateStoryInput
follow best practices for partial updates and look consistent with the rest of the schema.server/e2e/gql_me_test.go (1)
8-8
: Confirm continuity of server configurationSwitching from a custom-configured server to
Server(t, baseSeeder)
may alter environment setups (e.g., allowed origins). Please confirm that test security or cross-origin constraints remain correct under this new initialization method.server/e2e/ping_test.go (1)
9-9
: Ping test initialization streamlinedUsing
ServerPingTest(t)
simplifies setup without apparent downsides. The new approach looks fine, and the ping routes remain thoroughly tested.server/pkg/layer/encoding/exporter.go (1)
10-13
:❓ Verification inconclusive
Verify the impact of removing the Encoder field.
The removal of the
Encoder
field from theExporter
struct could be a breaking change that affects layer encoding functionality.
🏁 Script executed:
#!/bin/bash # Search for any references to the Encoder field rg -A 5 "Encoder\s+Encoder" # Search for any tests that might need updating rg -A 5 "TestExporter.*Encode"Length of output: 62
Verify the impact of removing the Encoder field
- The
Encoder
field has been removed from theExporter
struct and theEncode
method now returns nil, which suggests that the encoding functionality was effectively disabled.- Automated searches for references to the removed
Encoder
field and for tests related toTestExporter.*Encode
produced no output, so we cannot conclusively determine the impact from these searches alone.- Action: Please perform a manual verification to ensure that no other parts of the codebase depend on the previous encoding functionality and confirm that tests have been adequately updated to cover these changes.
server/pkg/asset/id.go (1)
10-26
: LGTM! Well-structured additions for project ID support.The new type alias and variables for ProjectID follow the existing patterns and maintain consistency with other ID types in the package.
server/pkg/nlslayer/feature_test.go (2)
9-24
: LGTM! Test updated correctly for API changes.The test has been properly updated to use
NewFeature
with an explicit ID, maintaining good coverage of the feature creation functionality.
1-1
: Inconsistency between PR objectives and actual changes.The PR description states that it adds Google Analytics parameters to the Story component, but the actual changes focus on project-based asset management and various refactoring. Please clarify this discrepancy.
Likely an incorrect or invalid review comment.
server/e2e/mock_test.go (1)
11-11
: LGTM, but verify PR objectives.The server initialization has been simplified using
ServerMockTest
, which is a good improvement. However, this change appears unrelated to the PR's stated objective of adding Google Analytics support to the Story component.Likely an incorrect or invalid review comment.
server/pkg/asset/asset.go (2)
18-18
: LGTM: Project field addition.The new
project
field is correctly defined as a pointer toProjectID
, allowing for optional project association.Likely an incorrect or invalid review comment.
34-40
: LGTM: Project getter and setter methods.The getter and setter methods are well-implemented, following proper encapsulation practices.
server/internal/usecase/interfaces/asset.go (2)
28-28
: LGTM: Project ID parameter addition.The
ProjectID
field is correctly added toCreateAssetParam
, maintaining consistency with the asset struct changes.Likely an incorrect or invalid review comment.
39-43
: LGTM: Method signature updates.The method signatures have been correctly updated to include project context:
FindByWorkspaceProject
now includes project filteringUpdate
method added for project associationImportAssetFiles
replacesUploadAssetFile
with project supportserver/pkg/asset/builder.go (1)
57-60
: LGTM: Project builder method.The
Project
method is well-implemented, following the builder pattern and maintaining the fluent interface style.Likely an incorrect or invalid review comment.
server/internal/infrastructure/mongo/mongodoc/asset.go (2)
35-40
: LGTM! Clean implementation of project ID handling.The project ID handling is well-implemented with proper nil checks and string conversion.
65-72
: LGTM! Proper error handling for project ID conversion.The error handling for project ID conversion is thorough and follows best practices.
server/pkg/storytelling/story_bulider.go (2)
124-127
: LGTM! Consider adding validation in Build method.The EnableGa method is well-implemented. Consider adding validation in the Build method to ensure trackingID is set when enableGa is true.
129-132
:✅ Verification successful
LGTM! Consider adding validation for tracking ID format.
The TrackingID method is well-implemented. Consider adding validation for the Google Analytics tracking ID format (UA-XXXXX-Y or G-XXXXXX).
🌐 Web query:
What is the format of a Google Analytics tracking ID?
💡 Result:
Google Analytics tracking IDs come in two primary formats depending on the version of Analytics used:
1. Universal Analytics (Legacy)
Format:
UA-XXXXXXXXX-Y
- Starts with "UA-", followed by 7–14 digits (
XXXXXXXXX
), a hyphen, and a 1–3 digit suffix (Y
)- Example:
UA-123456789-1
This ID was used for older Universal Analytics properties and is being phased out[4][5][6].
2. Google Analytics 4 (GA4)
Format:
G-XXXXXXXXXX
- Starts with "G-", followed by 8–12 alphanumeric characters
- Example:
G-A1B2CD34EF
This newer Measurement ID replaced tracking IDs in GA4 for connecting websites/apps to analytics properties[1][2][4][8].
Key Differences
Feature Universal Analytics (UA-) GA4 (G-) Purpose Legacy tracking system Modern cross-platform Structure Numeric-only suffix Alphanumeric code Installation Via tracking code Via Google tag Data Collection Session-based Event-based For GA4 implementations using Google Ads or other services, you might also encounter IDs starting with "AW-" (e.g.,
AW-123456789
)[3][9]. Always verify your ID through Admin > Data Streams > Web Stream Details in GA4[1][4][9].Citations:
- 1: https://www.monsterinsights.com/google-analytics-tracking-id-measurement-id-complete-guide/
- 2: https://analytify.io/google-analytics-4-measurement-id/
- 3: https://support.similarweb.com/hc/en-us/articles/7783050018589-How-to-find-Google-Analytics-Tracking-ID
- 4: https://www.optimizesmart.com/understanding-measurement-id-in-ga4-google-analytics-4/
- 5: https://quickcreator.io/quthor_blog/google-analytics-tracking-id-how-to-find-and-add/
- 6: https://www.427marketing.co.uk/blog/a-guide-to-google-analytics-tracking-id
- 7: https://www.blobr.io/how-to-guides/where-should-the-google-analytics-code-be-placed-on-your-website
- 8: https://www.semrush.com/blog/google-analytics-tracking-id/
- 9: https://support.google.com/analytics/answer/9539598?hl=en
- 10: https://community.hubspot.com/t5/Reporting-Analytics/Grab-the-Google-UA-code-in-the-new-GA4-format/m-p/391560
LGTM! The
TrackingID
method is well-implemented. However, to improve the robustness of the code, please consider adding validation logic that verifies the tracking ID conforms to accepted Google Analytics formats. For example:
- Universal Analytics (Legacy): Ensure it matches the pattern
UA-XXXXXXXXX-Y
, where the numeric part contains 7–14 digits and the suffix consists of 1–3 digits (e.g.,UA-123456789-1
).- Google Analytics 4 (GA4): Ensure it matches the pattern
G-XXXXXXXXXX
, starting with"G-"
followed by 8–12 alphanumeric characters (e.g.,G-A1B2CD34EF
).Adding such validation would help catch any improperly formatted IDs early in the process.
File:
server/pkg/storytelling/story_bulider.go
Lines: 129-132server/internal/usecase/interfaces/project.go (1)
74-75
: LGTM! Clear and descriptive method renaming.The method renaming from ExportProject/ImportProject to ExportProjectData/ImportProjectData better reflects their purpose. The signature changes improve type safety with idx.ID.
server/internal/adapter/gql/loader_asset.go (2)
39-54
: LGTM! Project ID handling is well-implemented.The addition of project ID filtering is implemented correctly with proper error handling and null checks.
60-66
: Performance optimization in edges initialization.Good optimization by pre-allocating the edges slice with the known size and using direct index assignment.
server/internal/infrastructure/memory/asset.go (1)
49-59
: LGTM! Project filtering logic is well-implemented.The implementation correctly handles both project-specific and workspace-only filtering scenarios.
server/e2e/gql_scene_test.go (2)
16-16
: LGTM! Response object access pattern is consistent.The updates to use
r.Object().Value(...)
maintain consistency across all response handling.Also applies to: 42-42, 71-73
77-100
: Verify the external image URL in tests.The function uses a hardcoded test URL which might not be available during test execution.
Consider using a more reliable test URL or mocking the external image endpoint.
server/internal/adapter/gql/gqlmodel/convert_project.go (1)
108-113
: LGTM! ProjectExport type is well-structured.The type definition includes essential fields with proper JSON tags and types.
server/internal/adapter/gql/gqlmodel/convert_storytelling.go (1)
35-36
: LGTM! GA fields added correctly.The Google Analytics fields
EnableGa
andTrackingID
are properly integrated into the Story struct, following the existing pattern and maintaining consistency with other fields.server/e2e/gql_validate_geojson_test.go (1)
186-186
: LGTM! Function call updated correctly.The
createAssetFromFileData
call has been properly updated to include the newprojectId
parameter as nil, maintaining backward compatibility while supporting the new project-specific asset handling feature.server/internal/usecase/interfaces/story.go (1)
38-39
: LGTM! GA fields added correctly to UpdateStoryInput.The optional GA fields
EnableGa
andTrackingID
are properly added as pointer types, consistent with other optional fields in the struct.server/internal/app/auth_client.go (1)
105-107
: LGTM! Good improvement to logging behavior.The conditional logging for e2e users helps reduce noise in logs during testing.
Also applies to: 115-117
server/pkg/storytelling/story.go (2)
41-42
: LGTM! Well-structured Google Analytics integration.The implementation properly encapsulates GA-related fields with appropriate getters and setters, aligning with the PR objectives.
Also applies to: 134-140, 190-196
57-62
: LGTM! Good defensive programming.The addition of the nil check in the
Pages()
method prevents potential nil pointer dereferences.server/internal/adapter/gql/resolver_query.go (1)
15-17
: LGTM! Clean implementation of project-specific asset filtering.The addition of the optional
projectId
parameter is well-implemented and follows GraphQL best practices.server/internal/infrastructure/mongo/mongodoc/storytelling.go (3)
34-35
: LGTM! New fields added for Google Analytics support.The addition of
EnableGa
andTrackingID
fields to theStorytellingDocument
struct aligns with the PR objectives.
87-88
: LGTM! Proper initialization of GA fields.The GA fields are correctly initialized from the Story instance.
197-198
: LGTM! GA fields properly included in model conversion.The GA fields are correctly included when building the Story model.
server/internal/app/app.go (1)
72-78
: Improved auth configuration handling with disabled auth support.The changes properly handle the case when authentication service is disabled by using a dummy middleware.
server/internal/infrastructure/fs/file.go (2)
113-113
: Improved path validation for asset removal.The path validation now correctly checks against the full asset directory path.
286-286
: LGTM! Proper URL path construction for assets.The URL path is correctly constructed by joining the base path with the asset directory and filename.
server/internal/infrastructure/fs/file_test.go (3)
116-117
: LGTM! Updated test base URL to match new URL structure.The test correctly uses the root path as the base URL, aligning with the changes in the implementation.
165-165
: LGTM! Updated test base URL for asset removal.The test correctly uses the root URL for asset removal validation.
316-316
: LGTM! Updated test base URL for URL generation.The test correctly uses the root URL for asset URL generation.
server/e2e/common.go (1)
45-45
: LGTM! Clear type definition.The
Seeder
type signature is well-defined and includes the necessary parameters.server/internal/adapter/gql/resolver_mutation_storytelling.go (1)
58-59
: LGTM! Google Analytics fields added correctly.The
EnableGa
andTrackingID
fields are properly added to theUpdateStoryInput
structure, aligning with the PR objectives.server/e2e/gql_project_test.go (1)
42-42
:❓ Verification inconclusive
Verify the updated test expectations.
The test assertions have been updated with new expected counts. Please ensure these changes align with the actual business logic changes:
- Are there new default projects being created?
- Has the pagination logic been modified?
Run the following script to verify the project counts:
Also applies to: 144-144, 585-585, 617-617, 620-620
🏁 Script executed:
#!/bin/bash # Description: Search for project creation logic to understand the count changes. # Test: Search for project creation in test setup rg -A 5 "createProject|projects\(" server/e2e/Length of output: 17956
Verify the updated test expectations for project counts in GraphQL tests
The updated assertions (e.g., at line 42 where
edges.Length().IsEqual(2)
is used) now expect revised project counts. The grep results confirm that project creation is handled consistently across various tests. Please double-check that these new default numbers and pagination behaviors (also affecting lines 144, 585, 617, and 620) are intentional and correctly aligned with the updated business logic. Specifically:
- Confirm that the additional projects created in setup are expected in the query results.
- Verify the pagination/filtering logic still returns the intended project subsets.
server/internal/usecase/interactor/storytelling.go (1)
188-194
: LGTM! Google Analytics support added.The implementation correctly handles the new Google Analytics parameters:
enableGa
for enabling/disabling Google AnalyticstrackingID
for setting the tracking IDserver/internal/usecase/interactor/nlslayer.go (2)
859-861
: LGTM! Improved feature ID generation.The code now explicitly generates feature IDs using
nlslayer.NewFeatureID()
, making the ID generation more transparent and maintainable.
1109-1134
: LGTM! Enhanced feature collection handling.The code improvements include:
- Better variable naming (
features
instead ofi
)- More explicit feature creation and property updates
- Clearer structure for handling feature collections
server/gql/asset.graphql (3)
5-5
: LGTM! Project association support added to Asset type.The schema correctly adds optional
projectId
fields to both theAsset
type andCreateAssetInput
, allowing assets to be associated with specific projects.Also applies to: 24-24
29-32
: LGTM! Asset update functionality added.The schema adds support for updating asset-project associations through:
- New
UpdateAssetInput
type- New
UpdateAssetPayload
type- New
updateAsset
mutationAlso applies to: 49-52, 84-84
75-75
: LGTM! Project-specific asset filtering added.The
assets
query now supports filtering byprojectId
, enabling project-specific asset management.server/gql/plugin.graphql (2)
26-28
: Missing GA parameters in Story types.The PR objectives mention adding Google Analytics parameters (
enableGa
andtrackingId
) to the Story component, but these fields are not present in the Story-related extension types.Likely an incorrect or invalid review comment.
20-30
:❓ Verification inconclusive
Verify the impact of removing the Cluster extension type.
Please ensure that there are no existing plugins or extensions using the Cluster type before removing it.
Run this script to check for any usage:
🏁 Script executed:
#!/bin/bash # Description: Search for any references to Cluster extension type # Test: Look for any code using Cluster extension type. Expect: No results. rg -i "type:\s*Cluster" rg -i "extensionType:\s*Cluster"Length of output: 110
Action Required: Recheck the "Cluster" Reference Impact on Plugin Extensions
- A grep search detected a reference to
cluster
inserver/pkg/builtin/manifest.yml
(e.g.,type: cluster
). This may indicate an existing usage that could be affected by removing the Cluster extension type.- The GraphQL schema in
server/gql/plugin.graphql
no longer defines Cluster, conflicting with the reference in the manifest.- Please manually verify whether this manifest reference is intended for a different purpose or if it should be updated or removed to align with the removal of the Cluster extension type.
server/gql/workspace.graphql (2)
8-14
: LGTM! Good addition of project filtering.The addition of the optional
projectId
parameter to the assets query allows for better filtering and organization of assets within teams.
15-21
: Skipping formatting changes.These changes are purely formatting-related and don't affect functionality.
Also applies to: 121-123
server/go.mod (1)
1-1
: PR changes don't match objectives.The PR objectives mention adding Google Analytics parameters (
enableGa
andtrackingId
) to the Story component, but the actual changes focus on:
- Removing Cluster from PluginExtensionType
- Adding projectId to Team.assets query
- Adding run-reset target to Makefile
- Updating go.mod dependencies
Please either:
- Update the PR description to match the actual changes, or
- Add the missing GA-related changes to the Story component
Likely an incorrect or invalid review comment.
server/e2e/gql_storytelling_test.go (6)
15-55
: Thorough end-to-end coverage for Story creation and updates
This test effectively covers creation, fetching, updating (with the new GA fields), and deletion of a story, validating fields post-update. The logic appears sound, and negative cases (e.g., fetching a non-existent story) seem adequately handled elsewhere.
57-152
: Comprehensive test scenario for StoryPage CRUD
This test exercises page creation, duplication, movement, and deletion, including checks for invalid page IDs. The coverage is extensive and verifies multiple page states.
154-206
: Clear block creation and reordering checks
The test thoroughly validates adding, moving, and removing blocks, ensuring block IDs appear or disappear as expected. No issues found.
208-229
: Property update assertions for blocks
This function verifies property setting and ensures correct reflection in subsequent fetches. It appears reliable and well-structured.
561-583
: Schema alignment for updateStory mutation
The updated mutation signature and accompanying variables correctly incorporateenableGa
andtrackingId
fields. This aligns with the new schema fields and is consistent with the test logic.
869-902
: Removal logic handling
Removing a block now excludes the unusedlast
parameter. The test confirms the block’s removal by checking the blocks array. Implementation looks straightforward and clear.server/gql/storytelling.graphql (3)
23-24
: Mandatory analytics fields
DeclaringenableGa
andtrackingId
as non-null is consistent with the requirement for always providing GA configuration in the schema. Confirm that all story creation paths supply valid defaults to avoid errors.
75-75
: Comment section update
Renaming the comment section to# Publishment
is minor and has no functional impact.
86-88
: Optional analytics parameters in UpdateStoryInput
These new optional fields align with the mandatory fields in the Story type, allowing partial updates without forcing analytics changes each time. This addition is coherent with typical GraphQL input conventions.
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: 9
🔭 Outside diff range comments (3)
server/gql/plugin.graphql (1)
20-30
:⚠️ Potential issueAdd Google Analytics parameters to Story-related types.
According to the PR objectives, the Story component should support Google Analytics parameters (
enableGa
andtrackingId
). However, these parameters are not defined in the schema. Consider adding a new type or extending existing types to support GA functionality.Here's a suggested implementation:
enum PluginExtensionType { PRIMITIVE WIDGET BLOCK VISUALIZER INFOBOX Story StoryPage StoryBlock InfoboxBlock } +type StoryGoogleAnalytics { + enableGa: Boolean! + trackingId: String! +}server/internal/usecase/interactor/asset.go (1)
154-182
: 🛠️ Refactor suggestionEnforce project permission checks in file import.
Similar to “Update,” ifproject != nil
, consider verifying that the user has permission to write to that project within the same workspace to prevent unauthorized file imports.server/pkg/layer/encoding/exporter.go (1)
37-42
:⚠️ Potential issueEmpty Encode method requires implementation.
The Encode method now returns nil without processing the merged layer. This could lead to unexpected behavior.
Please implement the encoding logic or document why encoding is skipped.
🧹 Nitpick comments (17)
server/e2e/gql_asset_test.go (2)
122-179
: Comprehensive project association tests.
The new test covers various scenarios, including moving assets between projects and removing project associations. You might want to add negative-case testing (e.g., invalid project ID) to bolster coverage.
242-256
: Asset removal logic.
The new function straightforwardly removes an asset. Consider adding a check for whether the asset ID belongs to the correct workspace or project if such constraints exist.server/internal/adapter/gql/resolver_mutation_project.go (2)
178-182
: Remove or clarify commented-out code
This block referencingpid2
is now commented out. If it's no longer needed, consider removing it to keep the codebase clean.
287-288
: Typo in function name
ImporPluginFile
might be a typo and should probably readImportPluginFile
.- if err := usecases(ctx).Plugin.ImporPluginFile(ctx, pid, fileName, file); err != nil { + if err := usecases(ctx).Plugin.ImportPluginFile(ctx, pid, fileName, file); err != nil {server/internal/usecase/interactor/scene.go (2)
711-713
: Consider handling the error fromurl.Parse(urlStr)
.
Currently, the code discards the potential parsing error, which may cause unexpected behavior ifurlStr
is invalid. Handle or log the error explicitly to avoid silent failures.- u, _ := url.Parse(urlStr) + u, parseErr := url.Parse(urlStr) + if parseErr != nil { + log.Warnfc(ctx, "Failed to parse URL: %v", parseErr) + continue + }
765-767
: Consistent approach with asset addition to ZIP.
Similar to the earlier widget block, this repeats the same safe pattern of verifying the field type before invokingAddZipAsset
. Consider using a more prominent log level thanInfo
if failed asset additions are crucial.server/internal/adapter/gql/gqlmodel/models_gen.go (2)
919-921
:PropertyFieldLink
new fields for dataset references.
The newly introduced dataset references can help in linking external data. Ensure that IDs referencing datasets are valid and handle the possibility of missing or partial dataset metadata.
1332-1334
: ExtendedUpdateAssetInput
to acceptAssetID
andProjectID
.
This addition allows assets to be reassigned to a different project or updated more flexibly. The approach appears coherent with the rest of the schema.server/pkg/nlslayer/feature_test.go (1)
9-24
: Consider renaming the test to match its current implementation.Since this test no longer uses
NewFeatureWithNewId
and instead demonstrates the usage ofNewFeature
with an explicit ID, consider renaming it to better reflect its purpose, e.g.,TestNewFeatureWithExplicitId
.-func TestNewFeatureWithNewId(t *testing.T) { +func TestNewFeatureWithExplicitId(t *testing.T) {server/internal/adapter/gql/gqlmodel/convert_asset.go (1)
12-16
: Consider a more concise implementation using conditional expression.The project ID handling can be simplified while maintaining the same functionality.
- var pid *ID - if project := a.Project(); project != nil { - pidValue := IDFrom(*a.Project()) - pid = &pidValue - } + pid := (*ID)(nil) + if project := a.Project(); project != nil { + id := IDFrom(*project) + pid = &id + }server/pkg/scene/builder/scene.go (1)
23-24
: Consider consistent naming between input parameters and struct fields.The struct uses
EnableGA
andTrackingID
while the input parameters useenableGa
andtrackingId
. Consider aligning these names for better maintainability.- EnableGA bool `json:"enableGa"` - TrackingID string `json:"trackingId"` + EnableGa bool `json:"enableGa"` + TrackingId string `json:"trackingId"`server/e2e/gql_user_test.go (1)
28-28
: Consider documenting the unused parameter.The
gateway.File
parameter is added for consistency but not used in the implementation.Add a comment explaining why this parameter exists:
-func baseSeederUser(ctx context.Context, r *repo.Container, f gateway.File) error { +// baseSeederUser creates test users and workspaces. +// f parameter is added for consistency with other seeder functions but not used here. +func baseSeederUser(ctx context.Context, r *repo.Container, f gateway.File) error {server/internal/app/app.go (1)
164-168
: Consider adding documentation for the dummy middleware.The
AuthMiddlewareDummy
function is well-implemented but lacks documentation explaining its purpose and usage.Add documentation to explain the function's purpose:
+// AuthMiddlewareDummy returns a middleware that bypasses authentication checks. +// This is useful for development or testing environments where authentication is not required. func AuthMiddlewareDummy() (func(http.Handler) http.Handler, error) { return func(next http.Handler) http.Handler { return next }, nil }server/e2e/common.go (3)
35-43
: Consider using a more descriptive name for the global variable.The variable name
fr
is not descriptive enough. Consider renaming it tofileRepo
orglobalFileRepo
to better indicate its purpose.-var fr *gateway.File +var globalFileRepo *gateway.File
266-271
: Add support for additional JSON types.The
JSONEqRegexpValue
function only handlesmap[string]interface{}
type. Consider adding support for arrays and other JSON types to make it more robust.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{}: return JSONEqRegexpInterface(t, actualData, expected) + case []interface{}: + return JSONEqRegexpInterface(t, actualData, expected) + default: + t.Logf("Unsupported type for JSON comparison: %T", actual.Raw()) + return false } - return false }
283-297
: Improve error handling in ValueDump.The function silently ignores JSON marshaling errors. Consider logging these errors to help with debugging.
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/usecase/interactor/project.go (1)
513-533
: Consider adding progress tracking for large exports.For large projects with many assets, consider adding progress tracking to provide feedback during the export process.
func (i *Project) ExportProjectData(ctx context.Context, projectID id.ProjectID, zipWriter *zip.Writer, operator *usecase.Operator) (*project.Project, error) { + var totalAssets int + var processedAssets int + prj, err := i.projectRepo.FindByID(ctx, projectID) if err != nil { return nil, errors.New("project " + err.Error()) } if prj.IsDeleted() { fmt.Printf("Error Deleted project: %v\n", prj.ID()) return nil, errors.New("This project is deleted") } // project image if prj.ImageURL() != nil { + totalAssets++ err := AddZipAsset(ctx, i.file, zipWriter, prj.ImageURL().Path) if err != nil { return nil, err } + processedAssets++ + fmt.Printf("Export progress: %d/%d assets\n", processedAssets, totalAssets) } return prj, nil }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
server/e2e/test.zip
is excluded by!**/*.zip
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 (107)
server/Makefile
(3 hunks)server/e2e/common.go
(7 hunks)server/e2e/dataset_export_test.go
(0 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_custom_property_test.go
(1 hunks)server/e2e/gql_layer_test.go
(0 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_nlslayer_test.go
(0 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 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/storytelling.graphql
(3 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_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_plugin.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_scene.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 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
(9 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
(3 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 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/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/layer.go
(0 hunks)server/internal/usecase/interactor/nlslayer.go
(3 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/scene.go
(5 hunks)server/internal/usecase/interactor/scene_test.go
(0 hunks)server/internal/usecase/interactor/storytelling.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 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/nlslayer/feature.go
(0 hunks)server/pkg/nlslayer/feature_test.go
(1 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)server/pkg/scene/builder/story.go
(1 hunks)server/pkg/scene/builder_test.go
(0 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)web/src/services/api/propertyApi/utils.ts
(1 hunks)
⛔ Files not processed due to max files limit (6)
- web/src/services/api/sceneApi.ts
- web/src/services/gql/fragments/dataset.ts
- 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/scene.ts
💤 Files with no reviewable changes (47)
- server/internal/adapter/gql/gqlmodel/convert.go
- server/pkg/scene/builder_test.go
- server/internal/adapter/gql/gqlmodel/convert_plugin.go
- server/internal/usecase/interactor/project_test.go
- server/internal/adapter/gql/resolver_property_test.go
- server/pkg/layer/layerops/processor_test.go
- server/pkg/nlslayer/feature.go
- server/internal/infrastructure/mongo/mongodoc/scene.go
- server/internal/adapter/gql/gqlmodel/convert_scene.go
- server/pkg/scene/builder/encoder_test.go
- server/internal/adapter/gql/gqlmodel/convert_dataset.go
- server/e2e/dataset_export_test.go
- server/e2e/gql_nlslayer_test.go
- server/internal/adapter/gql/context.go
- server/e2e/gql_layer_test.go
- server/internal/adapter/gql/gqlmodel/models.go
- server/e2e/gql_project_export_test.go
- server/gql/property.graphql
- server/internal/adapter/gql/resolver_mutation_scene.go
- server/pkg/scene/builder/builder_test.go
- server/internal/adapter/gql/gqlmodel/convert_tag.go
- server/internal/adapter/gql/gqldataloader/dataloader.go
- server/gqlgen.yml
- server/gql/cluster.graphql
- server/internal/adapter/gql/loader.go
- server/internal/usecase/interactor/scene_test.go
- server/internal/adapter/gql/resolver_mutation_dataset.go
- server/internal/adapter/gql/gqlmodel/convert_layer.go
- server/internal/adapter/gql/resolver_dataset.go
- server/internal/adapter/gql/resolver_layer.go
- server/internal/adapter/gql/resolver_scene.go
- server/gql/dataset.graphql
- server/internal/adapter/gql/resolver_mutation_tag.go
- server/gql/tag.graphql
- server/internal/adapter/gql/resolver_tag.go
- server/internal/adapter/gql/loader_dataset.go
- server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
- server/internal/adapter/gql/resolver_mutation_layer.go
- server/internal/adapter/gql/gqldataloader/tagloader_gen.go
- server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
- server/internal/adapter/gql/resolver_dataset_schema.go
- server/internal/adapter/gql/resolver_property.go
- server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
- server/gql/layer.graphql
- server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
- server/internal/usecase/interactor/layer.go
- server/internal/adapter/gql/loader_tag.go
🧰 Additional context used
🧠 Learnings (2)
server/internal/adapter/gql/resolver_mutation_project.go (2)
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`.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
🔇 Additional comments (183)
server/gql/plugin.graphql (1)
99-99
: LGTM!The formatting changes improve readability.
Also applies to: 109-109
server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go (5)
11-19
: LGTM! Improved feature creation with explicit ID generation.The change from
NewFeatureWithNewId
toNewFeature
with explicit ID generation provides better control and clarity over feature ID management.
21-29
: LGTM! Consistent implementation of the new feature creation pattern.The changes maintain consistency with the new explicit ID generation pattern.
31-39
: LGTM! Consistent implementation maintained.The changes follow the same explicit ID generation pattern consistently.
41-49
: LGTM! Pattern consistency maintained.The changes maintain the same explicit ID generation pattern.
51-62
: LGTM! Pattern consistency maintained with proper geometry composition.The changes maintain the same explicit ID generation pattern while preserving the correct geometry composition in the GeometryCollection.
server/internal/usecase/interactor/asset.go (2)
39-44
: Consider validating project readability.
Currently, the code checks for readable workspaces but not specifically whether the user can read the project ifpid
is present. You might want to ensure operators have permission to read the project as well.
91-91
: Association with project on creation.
Assigning the project ID here is consistent. Ensure that any external calls or references, if any, are also updated to handle the new project association.server/internal/adapter/gql/resolver_query.go (1)
15-16
: Confirm alignment with updated usecase method.
This newprojectId
parameter correctly threads through to the underlying data loader. It’s consistent with the asset interactor changes. Good job!server/e2e/gql_asset_test.go (7)
15-15
: No functional change.
This comment line is informational, and there’s nothing to address further.
17-120
: Verify the coreSupport filtering logic.
At line 114, the test expects all returned assets to havecoreSupport = true
, but the setup also creates assets withcoreSupport = false
. Ensure the correct retrieval logic is invoked; otherwise, the test might pass or fail unexpectedly.
181-199
: Updated GraphQL mutation.
IncludingprojectId
in the create mutation is aligned with the asset interactor changes. This addition seems correct.
200-222
: File upload error handling.
Consider verifying the behavior when the file does not exist or the project ID is invalid. Handling these cases in tests can increase confidence in robustness.
224-241
: Update mutation looks good.
The GraphQL layer to set (or remove) a project ID is consistent with the new interactor logic.
258-285
: Programmatic file creation testing.
Creating temporary files for tests ensures the approach is realistic. Good approach for broad coverage.
287-350
: Granular asset query.
TheprojectId
filter here matches the updated schema requirements. This is a clean approach to ensure consistent test coverage of new arguments.server/internal/adapter/gql/resolver_mutation_project.go (33)
10-12
: New imports for URL and path operations
Adding"net/url"
and"path"
is appropriate for URL parsing and file path extraction.
130-130
: Validate generated zip filename to prevent collisions
Using the project ID directly in the zip filename is generally fine, but consider sanitizing or validating the string to prevent collisions or invalid filenames.
155-155
: Consistent rename to ExportProjectData
The call toExportProjectData
aligns with the revised function signature, indicating a clear purpose. Ensure all callers are updated accordingly.
183-183
: Confirm performance for retrieving all project assets
FindByWorkspaceProject
with a nil filter might lead to large asset sets. Ensure that this is intentional and does not impair performance.
189-194
: Checks for potential filename collisions
Parsing the asset URL and inferring the filename is fine, but confirm whether multiple assets could produce identical filenames, overwriting each other in theassetNames
map.
197-199
: Straightforward use of data map
Storing project, plugins, and schema references in the main data map is clear and maintainable. No issues here.
212-212
: New ImportProject function
This new mutation method is a logical addition, providing symmetrical functionality for export/import flows.
216-219
: Validate ZIP content for security
When uncompressing user-provided files, ensure there is no risk of writing files outside the intended directory.
220-220
: Consistent operator extraction
Storing the operator in a local variable keeps the pattern consistent and simplifies subsequent calls.
223-223
: Explanatory comment
The comment clarifies why creating a project first is necessary. This is helpful context.
225-225
: Robust approach for project import
CallingImportProjectData
to create a project from metadata is well-structured.
226-227
: Good error handling
Returning a custom message ensures clarity for debugging.
229-231
: Transactional approach
Safely rolling back iftx.End
fails is good practice.
242-244
: Runtime validation of project ID
Safely converting project IDs ensures consistent internal representation.
249-249
: Map real asset name
Retrieving the real name fromassetNames
is correct, but be sureassetNames[beforeName]
always exists to avoid empty strings.
252-254
: ImportAssetFiles usage
The approach is consistent with the new import flow. Check for large or invalid files that might disrupt the system.
257-265
: Updating project image
Conditionally updating the project's image is a sensible approach, and you handle errors properly.
267-268
: Ensuring placeholders are replaced
Replacing the old filename with the new is correct for patching references in JSON. No obvious issues.
272-272
: Scene creation for project
Creating a new scene post-import is a solid approach, ensuring references align with the newly imported project.
280-280
: Parsing plugin file name
Splitting the path into parts is straightforward. Verify the array indices are always valid.
283-285
: Proper plugin ID conversion
Converts the newly constructed plugin ID. This is correct.
292-292
: Load plugin/scene data
Reading plugin & scene data in one go is convenient. No concerns here.
294-296
: Importing plugins
Centralizing plugin imports is consistent. Investigate logs or metrics for large, time-consuming imports.
299-301
: Import scene
Bringing scene data back in is well-named and methodical.
304-306
: Import NLSLayers
Be mindful of potential conflicts if data references an existing layer with the same ID.
309-311
: Import styles
Style import logic is consistent with the overall approach.
314-316
: Import story
Supports the storytelling flow in the new project. Watch for null references in scene data.
319-319
: Commit transaction
Explicitly committing the transaction after all subtasks is good practice.
334-334
: End of ImportProject method
No additional logic here. Just the closing brace.
335-344
: Scene ID replacement helper
replaceOldSceneID
cleanly handles references to the old ID in JSON. The approach is straightforward and easy to maintain.
346-354
: unmarshalProject
Extractsproject
from the JSON. Consider ensuring the top-level “project” key is always present before ignoring errors.
356-370
: unmarshalAssets
Captures asset name mappings. Handling potential absent keys or invalid structures is recommended.
372-382
: unmarshalPluginsScene
Cleanly organizes plugin, scene, and schema data. Watch for type assertions returning nil.server/e2e/gql_project_export_import_test.go (25)
4-8
: Additional imports for JSON and HTTP
Adding encoding/json, fmt, and net/http is expected for the new export/import test flow.
12-13
: New references to id and idx packages
These appear to support ID validations within tests. Good for ensuring correctness.
19-19
: Updated test run command
Comment clarifies how to run the new test. This is helpful.
21-21
: Rename test to TestProjectExportImport
Reflects the consolidated coverage of both export and import in a single test.
23-23
: Switching to fullSeeder
Enables a richer data context for the test. Slightly increases complexity but ensures thorough coverage.
25-29
: getScene usage
Initial check on seeded data correctness. This ensures the baseline is as expected before export.
31-39
: Exporting the data
exporProject
function is introduced to test actual project data export, returning a filename. Straightforward approach.
41-42
: Asserting project name & description
Checking the newly imported data's name and description ensures correct round-trip behavior.
44-44
: Store new scene ID
Saving the newly returned scene ID for further validation is a logical step.
46-47
: Retrieving imported scene
getScene
is reused to confirm the newly created scene's correctness.
49-49
: Structured approach to comparing results
UsingcompareValue
repeatedly fosters granular checks for each property.
50-57
: Compare scene properties
Examines multiple fields (styles, widgets, stories, newLayers, etc.) individually. This ensures thorough coverage.
58-63
: Deferred cleanup of file
Removes the exported zip after test completion, avoiding clutter.
64-68
: convertLine function
Flattens object fields into lines for segment-wise comparison. Straightforward.
71-102
: compareValue function
Detailed line-by-line comparison helps isolate differences. The concept of ignoringid
orpropertyId
is pragmatic.
103-106
: isIgnore function
Allows skipping lines based on property name prefixes, maintaining test focus on other fields.
109-109
: General ID check
isID
discards lines if they are recognized as random IDs. This prevents spurious test failures.
112-119
: UUID detection
Usingidx.From[id.Widget]
to verify the format is interesting. Confirm no side effects if ID is from another domain.
121-139
: exporProject function
Performs the GraphQL mutation, downloads the ZIP, and writes it locally. The flow is a good representation of real usage.
141-141
: importProject function signature
Returning*httpexpect.Value
is consistent with the rest of the test’s pattern.
142-142
: Opening the ZIP for upload
File handling is correct and includes defers for closing. No sign of concurrency issues.
169-169
: Project data extraction
Validates that theimportProject
response includesprojectData
. This is the crux of the import test.
172-172
: *Refactor getScene to return httpexpect.Value
Maintains test uniformity across retrieval methods.
182-182
: Ensure node is present
v.NotNull()
checks for a valid scene node, catching import failures.
185-185
: Return the value
No further logic needed. Straightforward final step.server/e2e/seeder.go (25)
5-9
: New crypto/rand & JSON logic
Generating UUIDs and handling JSON content are typical steps for dynamic seeding.
11-12
: OS file handling
Ensuring we have the correct file path and environment requirements for seeding.
15-16
: Expanded references to usecase/gateway
Indicates heavier usage of the file gateway for uploading assets.
18-20
: New usage of asset package
Creating assets directly within the seeder.
22-25
: Adopting nlslayer & plugin references
Lays the foundation for more robust seeding with advanced features.
46-46
: Project description appended
pDesc
is updated to include the project name and "desc" suffix, clarifying the test project’s purpose.
49-49
: Mocking a fixed time
now
ensures consistent seeds across runs for repeatable e2e results.
51-52
: Root layer initialization
These new IDs for root layers enable layering within scenes.
53-55
: Story block entity IDs
IDs for story, page, and block are introduced to seed basic storytelling data.
58-58
: baseSeeder signature changed
Now acceptinggateway.File
to handle asset uploads.
70-70
: Refactoring baseSetup
Centralizes user and workspace creation along with asset usage.
73-73
: Handling language support
baseSeederWithLang(..., lang)
ensures we can seed data for multi-lingual tests.
88-88
: Updated baseSetup
Moved logic for adding assets to a separate function. Improves clarity.
102-106
: Create & store a default asset
Ensures the test project has at least one default image.
125-167
: addAsset function
Opens the local file, detects MIME type, and uploads it withgateway.File
. Then builds an asset domain object.
169-183
: fullSeeder function
ExtendsbaseSetup
with additional layers, widgets, stories, etc. for more complex testing scenarios.
185-230
: fullSetup
Incorporates the root layer, property creation, plugin associations, widget additions, storytelling, and more.
232-281
: addWidget
Creates a sample widget from the official plugin. Logic checks for extended mode, location, etc.
283-312
: addStory
Builds a property for a story, creates a single page, and saves it. This is the seed for a multi-page story scenario.
314-338
: _createPage
Establishes a minimal page with one block, suitable for nested storytelling.
340-368
: _createBlock
Constructs a textual story block from the "textStoryBlock" extension. Good error handling for plugin retrieval.
370-393
: addLayerStyle
Generates a style property and saves it, laying ground for advanced layer styling in scenes.
395-448
: addLayerSimple
Adds a simple layer, which can hold sketches and configurations.
450-510
: addGeoJson
Imports geometry data from JSON, transforms it into a feature, and updates the existing simple layer’s sketch info.
512-527
: generateUUID
Creates a safe, collision-resistant UUID for seeding objects.server/internal/usecase/interactor/scene.go (3)
22-22
: New storytelling import looks appropriate.
The newly introduced import fromstorytelling
aligns with the usage of story-related logic in this file.
680-683
: Safe nil-check forstoryList
but only the first story is used.
This conditional ensures thatstoryList
has at least one entry before dereferencing. Confirm that only handling the first story is intentional, as any additional stories will be ignored.
740-741
: Type-check for URL usage is valid.
This conditional elegantly handles fields that may or may not be URLs. Proceeding only when the data is indeed a*url.URL
avoids potential type mismatches.server/e2e/gql_storytelling_test.go (5)
15-55
: TestStoryCRUD ensures story creation, updating, and deletion.
These tests verify initial story count, creation/update of a story (including GA fields), and final deletion. This structure looks robust and covers key mutations and queries.
57-152
: TestStoryPageCRUD thoroughly checks page creation, duplication, moves, and deletions.
This suite demonstrates comprehensive coverage of CRUD operations on pages, including boundary cases such as invalid page IDs, as well as verifying updated order of pages.
154-207
: TestStoryPageBlocksCRUD validates block creation, movement, and removal.
These assertions confirm that blocks can be dynamically inserted and reordered, ensuring the page structure updates as expected.
208-229
: TestStoryPageBlocksProperties confirms property updates on blocks.
This ensures property fields (e.g., text, padding) can be updated correctly and then properly retrieved. Coverage is good for typical property fields.
231-413
: TestStoryPublishing checks final publishing workflow and data retrieval.
The test publishes a story and verifies the serialized content (including GA defaults) from both internal read calls and external endpoint checks. This comprehensively validates the final publishing flow.server/internal/adapter/gql/gqlmodel/models_gen.go (4)
581-588
: Expanded fields inMergedPropertyField
.
The structure now referencesSchemaID
,FieldID
, and so on. The approach of storing both value and type details in a single struct is consistent. Just ensure that all references (Schema, Field, etc.) remain synchronized in usage.
[approve]
906-915
:PropertyField
captures more detailed metadata.
This expansion forPropertyField
(addingLinks
,Type
,Value
, etc.) provides deeper control over property data. Validate that the new references to parent objects are never nil in usage.
1221-1222
: New GA fields inStory
.
The addition ofEnableGa
andTrackingID
fits the PR’s objective of integrating Google Analytics. Implementation looks consistent with usage in the tests.
1337-1339
: MatchingUpdateAssetPayload
changes to reflect new project association.
The payload now echoes updatedAssetID
andProjectID
, ensuring clients can confirm changes to the asset’s project linkage.server/e2e/gql_me_test.go (1)
8-8
:❓ Verification inconclusive
Verify the impact of simplified server initialization.
The server initialization has been simplified, but we need to ensure that the new
Server
function maintains the necessary configuration for authentication in tests.
🏁 Script executed:
#!/bin/bash # Search for Server function definition to verify auth config ast-grep --pattern 'func Server($_, $_) { $$$ }'Length of output: 52
🏁 Script executed:
#!/bin/bash # Search for the definition of the Server function along with its surrounding context # to manually inspect if authentication configuration is maintained. rg -n "func Server(" .Length of output: 99
Review and verify Server initialization for auth requirements.
The change simplifies server startup by switching from
StartServer
toServer
. Unfortunately, our automated checks couldn’t retrieve the Server function’s implementation details. Please manually verify that the newServer
function still includes the necessary authentication configuration for the tests.server/e2e/ping_test.go (1)
9-9
:❓ Verification inconclusive
Verify CORS configuration in ServerPingTest.
The server initialization has been simplified to
ServerPingTest
. Ensure that CORS settings are properly configured for the test environment.
🏁 Script executed:
#!/bin/bash # Search for ServerPingTest function definition to verify CORS config ast-grep --pattern 'func ServerPingTest($_, $_) { $$$ }'Length of output: 60
Attention: Verify CORS configuration in ServerPingTest Initialization
The server initialization has been simplified to use
ServerPingTest
, and it’s crucial to ensure that the test environment still applies the expected CORS settings. Please manually verify that:
- CORS-related configurations (e.g., headers or middleware) are explicitly set within or prior to invoking
ServerPingTest
.- The behavior aligns with similar configurations in other parts of the codebase (like
gql_me_test.go
).
🏁 Script executed:
#!/bin/bash # Enhanced search for the definition of ServerPingTest in Go files, # ignoring parameter details to capture all variations. rg -n "func[[:space:]]\+ServerPingTest" -t go . # Additionally, search within the located files for any CORS configuration or keywords. rg -n "CORS" -t go .Length of output: 154
Verify that CORS settings are correctly applied in ServerPingTest initialization
It appears that the test server is now initialized via
ServerPingTest
(as seen inserver/e2e/ping_test.go
), but our search did not reveal the function’s definition directly. We did observe a CORS configuration in./server/internal/app/app.go
(line 54), which suggests that CORS is set up at the application level. Please verify that:
- ServerPingTest Initialization: Either directly applies the expected CORS middleware or incorporates the standard app initialization that includes CORS.
- CORS Consistency: The configuration applied here aligns with what’s done in similar tests (e.g., in
gql_me_test.go
) and ensures that CORS headers are present during testing.Manually confirm that the test environment reflects these settings or update the initialization if necessary.
server/internal/usecase/repo/asset.go (2)
1-1
: Verify alignment with PR objectives.The changes in these files appear to be refactoring-related and don't directly contribute to the stated objective of integrating Google Analytics parameters into the Story component. Consider splitting these changes into a separate PR to maintain focus on the GA integration.
20-20
:❓ Verification inconclusive
Verify all callers are updated for the new method signature.
The method has been renamed from
FindByWorkspace
toFindByWorkspaceProject
with an additionalProjectID
parameter. This is a breaking change.
🏁 Script executed:
#!/bin/bash # Search for FindByWorkspace usage in the codebase rg "FindByWorkspace\(" --type go # Search for FindByWorkspaceProject usage to verify migration rg "FindByWorkspaceProject\(" --type goLength of output: 4963
Ensure All Call Sites Properly Delegate to the Updated Asset Method
The asset repository method in
server/internal/usecase/repo/asset.go
has been renamed fromFindByWorkspace
toFindByWorkspaceProject
and now requires an additionalProjectID
parameter. Please verify that:
- All direct calls to the asset repository (e.g. in the interactor, and in the infrastructure layers such as the memory and mongo implementations) now correctly use or delegate to
FindByWorkspaceProject
.- The GraphQL adapter/wrapper in
server/internal/adapter/gql/loader_asset.go
correctly callsc.usecase.FindByWorkspaceProject
even if the externally exposed method remains namedFindByWorkspace
—ensuring no accidental bypass of the new signature.- Test files (e.g.
server/e2e/gql_asset_test.go
) and all other callers have been updated to prevent any mismatch caused by the breaking signature change.Once you’ve confirmed that all these integrations and delegations are working as expected, this change will be safe to merge.
server/pkg/asset/id.go (1)
10-10
: LGTM! Well-structured additions for project ID handling.The new ProjectID type and its associated helper functions follow the established pattern, maintaining consistency with existing ID types.
Also applies to: 14-14, 18-18, 22-22, 26-26
server/e2e/mock_test.go (1)
11-11
: LGTM! Good refactoring to use shared server initialization.The change simplifies the test setup by using
ServerMockTest
, reducing code duplication while maintaining the same test behavior.server/internal/adapter/gql/gqlmodel/convert_asset.go (1)
12-16
: LGTM! Proper handling of project ID association.The implementation correctly handles nil checks and pointer conversions for the project ID.
server/pkg/asset/asset.go (2)
18-18
: LGTM! The new field follows Go best practices.The
project
field is properly encapsulated as a private field with a pointer type, allowing for optional project associations.
34-40
: LGTM! The getter and setter methods are well-implemented.The methods follow Go's idiomatic approach to encapsulation and provide a clean interface for accessing and modifying the project field.
server/internal/usecase/interfaces/asset.go (2)
28-28
: LGTM! The new field is properly typed.The
ProjectID
field is correctly defined as a pointer type, making it optional and consistent with the project association feature.
39-39
: LGTM! Method signatures are clear and consistent.The updated method signatures properly handle project associations:
FindByWorkspaceProject
clearly indicates its enhanced functionalityUpdate
method includes project ID modificationImportAssetFiles
consistently includes project ID parameterAlso applies to: 41-41, 43-43
server/internal/adapter/gql/resolver_team.go (1)
27-29
: LGTM! The resolver update is consistent with the project association feature.The
Assets
method signature and loader call are properly updated to include the project ID parameter while maintaining existing pagination functionality.server/pkg/asset/builder.go (1)
57-60
: LGTM! Project field addition looks good.The implementation of the
Project
method in the Builder follows the established pattern of other builder methods, maintaining consistency in the codebase.server/internal/infrastructure/mongo/mongodoc/asset.go (3)
17-17
: LGTM! Project field addition to AssetDocument.The optional Project field is correctly defined as
*string
to handle null values in MongoDB.
36-40
: LGTM! Project ID conversion in NewAsset.The conversion of Project ID to string is properly handled with null checks.
65-72
: LGTM! Project ID parsing in Model method.The conversion from string to ProjectID is properly handled with error checking.
server/internal/adapter/gql/resolver_mutation_asset.go (2)
19-26
: LGTM! Project ID handling in CreateAsset.The conversion of GraphQL input to internal ProjectID is properly handled with error checking.
41-62
: LGTM! UpdateAsset mutation implementation.The UpdateAsset mutation correctly handles the conversion of asset and project IDs with proper error handling.
server/pkg/scene/builder/story.go (2)
45-45
: LGTM! Title field addition to storyJSON.The Title field is correctly added to the JSON output.
1-84
: Inconsistency between PR objectives and actual changes.The PR objectives state that this PR adds Google Analytics support to the Story component by adding
enableGa
andtrackingId
fields. However, the actual changes in the code are about:
- Adding Project field support to Assets
- Adding Title field to storyJSON
This suggests that either:
- The wrong PR description was provided
- The wrong files were provided for review
- The GA-related changes are missing
Likely an incorrect or invalid review comment.
server/pkg/storytelling/story_bulider.go (1)
124-132
: LGTM! Clean implementation of GA support.The new builder methods for Google Analytics support are well-implemented, following the existing builder pattern consistently.
server/internal/usecase/interfaces/project.go (2)
46-47
: LGTM! GA fields properly added to UpdateProjectParam.The GA fields are correctly defined as nullable pointers, allowing for optional updates.
74-75
: LGTM! Method renames and type safety improvements.The method renames improve clarity, and the use of
idx.ID[accountdomain.Workspace]
enhances type safety.server/internal/adapter/gql/loader_asset.go (2)
39-55
: LGTM! Project ID filtering properly implemented.The project ID handling is well-implemented with proper null checks and error handling.
60-66
: Performance improvement in edge creation.Pre-allocating the edges slice with the known size is a good optimization.
server/internal/infrastructure/memory/asset.go (2)
49-59
: LGTM! Project filtering properly integrated.The project ID filtering is well-integrated with the existing workspace filtering logic, maintaining backward compatibility.
55-57
:❓ Verification inconclusive
Consider adding a test for project filtering.
While the implementation looks correct, it would be good to add unit tests specifically for the project filtering logic.
🏁 Script executed:
#!/bin/bash # Check if there are any tests for project filtering rg -l "TestFindByWorkspaceProject" server/internal/infrastructure/memory/Length of output: 73
Action Required: Add or Verify Unit Tests for Project Filtering
The project filtering logic in
server/internal/infrastructure/memory/asset.go
(lines 55–57) currently lacks dedicated tests. A search for a test named “TestFindByWorkspaceProject” returned no results, so please verify manually if tests for project filtering exist elsewhere. If not, it's advisable to add unit tests to specifically cover cases such as:
- Handling non-nil project IDs and nil projects.
- Verifying matching against provided project IDs.
- Confirming the behavior when a keyword filter is applied.
server/e2e/gql_scene_test.go (2)
16-16
: LGTM! Improved response object access pattern.The changes to use
.Object().Value()
make the code more robust by ensuring proper type checking at each level of the response object.Also applies to: 42-42, 71-73
77-100
: LGTM! Well-structured project creation function.The new
createProjectWithExternalImage
function is well-implemented with:
- Clear GraphQL mutation structure
- Proper error handling
- Type-safe response handling
server/internal/adapter/gql/gqlmodel/convert_project.go (2)
108-113
: LGTM! Well-structured export type.The
ProjectExport
type is well-designed with:
- Appropriate field types
- Clear JSON tags
- Optional URL handling
115-137
: LGTM! Robust conversion functions.Both
ToProjectExport
andToProjectExportFromJSON
functions include:
- Proper nil checks
- Clean error handling
- Type-safe conversions
server/internal/adapter/gql/gqlmodel/convert_storytelling.go (1)
35-36
: LGTM! Successfully integrated GA support.The addition of
EnableGa
andTrackingID
fields properly implements the PR objective of adding Google Analytics support to the Story component.server/e2e/gql_validate_geojson_test.go (1)
186-186
: LGTM! Updated function call with project ID parameter.The addition of the
nil
project ID parameter aligns with the enhanced asset management capabilities while maintaining existing test functionality.server/internal/usecase/interfaces/story.go (1)
38-39
: LGTM! Google Analytics fields added correctly.The new fields
EnableGa
andTrackingID
are properly implemented as optional fields using pointers, which aligns with the existing pattern for update operations.server/pkg/scene/builder/builder.go (1)
181-181
: LGTM! Google Analytics parameters correctly propagated.The GA parameters are properly passed through to the
sceneJSON
method, maintaining the flow of configuration data.server/internal/infrastructure/mongo/asset.go (1)
67-101
: LGTM! Project-based filtering properly implemented.The method has been correctly updated to support filtering by both workspace and project ID. The filter construction is well-structured with proper handling of both cases.
server/internal/app/auth_client.go (2)
105-107
: LGTM! Improved security by filtering sensitive logs.The condition prevents logging sensitive user information during e2e tests.
115-117
: LGTM! Consistent filtering of operator logs.The condition maintains consistency with the user log filtering above.
server/pkg/storytelling/story.go (1)
58-60
: LGTM! Added nil check to prevent panic.The nil check in Pages() method improves robustness.
server/internal/infrastructure/mongo/mongodoc/storytelling.go (4)
34-35
: LGTM! Added Google Analytics fields.The fields match the Story struct definition.
87-88
: LGTM! Consistent field initialization.The GA fields are properly initialized from the Story model.
197-198
: LGTM! Proper field mapping in Model method.The GA fields are correctly mapped when building the Story model.
226-231
: LGTM! Improved variable naming.Renamed variable from
page
toblock
for better clarity.server/internal/app/app.go (1)
72-78
: LGTM! Authentication handling is well-structured.The changes introduce a flexible authentication mechanism that allows for a mock authentication path when the authentication server is disabled, while maintaining the original authentication flow when enabled.
server/internal/infrastructure/fs/file.go (2)
113-113
: LGTM! Path validation is secure and accurate.The path validation logic correctly checks that the asset path is within the expected directory structure.
286-286
: LGTM! URL path construction is simplified.The URL path construction is simplified while maintaining the correct path structure.
web/src/services/api/propertyApi/utils.ts (1)
247-248
:❓ Verification inconclusive
Verify the impact of removing
actualValue
.The removal of
actualValue
from the merged property field type might affect code that relies on this field.Run the following script to check for any remaining usages of
actualValue
:
🏁 Script executed:
#!/bin/bash # Description: Check for any remaining usages of `actualValue` in the codebase. # Test: Search for any remaining usages of `actualValue`. Expect: No occurrences. rg -A 5 $'actualValue'Length of output: 13864
Action Required: Confirm No Unintended Impact from Removing
actualValue
The removal of
actualValue
from the merged property field type inweb/src/services/api/propertyApi/utils.ts
appears correct. However, the codebase still contains several references toactualValue
that may indicate related functionality:
GraphQL Fragments:
- Files like
web/src/services/gql/__gen__/gql.ts
andweb/src/services/gql/fragments/property.ts
still show a commented-out# actualValue
. Verify that this auto-generated/commented code does not cause any discrepancies with the current schema expectations.Server Configuration:
- In
server/gqlgen.yml
,actualValue
is still configured withresolver: true
. Confirm whether the GraphQL schema and resolvers need updating to reflect the removal.Testing and Utility Code:
- In test files (e.g.,
server/internal/infrastructure/mongo/mongodoc/nlslayer_test.go
) and the flag generation code (server/tools/cmd/gen/flag.go
), there are usages ofactualValue
(function calls and variable assignments). Please double-check if these usages are still necessary or if they should be refactored in line with the change.Please verify these areas to ensure no dependent functionality is inadvertently broken.
server/internal/infrastructure/fs/file_test.go (3)
116-117
: LGTM! Test case updated to match new URL handling.The base URL has been correctly updated to use the root path, aligning with the changes in the main code.
165-165
: LGTM! Base URL updated consistently.The base URL has been consistently updated across test cases.
316-316
: LGTM! Test case updated for URL path construction.The test case has been updated to reflect the simplified URL path construction.
server/e2e/gql_custom_property_test.go (1)
347-762
: LGTM! Query structure improvements.The GraphQL query has been significantly improved:
- Better organization using fragments
- Clear separation of concerns
- Improved readability with proper indentation
- Comprehensive coverage of all required fields
server/e2e/gql_project_test.go (3)
144-144
:✅ Verification successful
Verify the test expectation change.
The test expectation for the number of edges has been increased from 4 to 5. Ensure this change aligns with the project creation calls in the test.
🏁 Script executed:
#!/bin/bash # Description: Check the project creation calls in the test. # Search for project creation calls in the test rg -A 5 "createProject\(" server/e2e/gql_project_test.goLength of output: 2566
Test Expectation Verified: 5 Edges Expected
The test now creates 5 projects (as verified by the 5 distinct
createProject
calls), so updating the edge length check toedges.Length().IsEqual(5)
aligns with the current test setup. Please ensure that any future modifications to project creation maintain this consistent expectation.
585-585
:❓ Verification inconclusive
Verify the test expectation changes.
Multiple test expectations have been updated:
- Total count changed from 20 to 21
- Total count changed from 4 to 5
- Expected edges length changed from 4 to 5
Ensure these changes align with the test data setup.
Also applies to: 617-617, 620-620
🏁 Script executed:
#!/bin/bash # Description: Check the test data setup to verify the expected counts. # Search for project creation in test data setup rg -A 5 "projects\(t, ctx, r, " server/e2e/gql_project_test.goLength of output: 813
Action: Confirm Updated Test Expectations Against Data Setup
The test now asserts updated values:
- The total project count expectation was modified from 20 to 21.
- Other expectations were adjusted (total count from 4 to 5 and edges length from 4 to 5).
Please ensure these updated assertions properly reflect the intended test data. For instance, the grep output in
server/e2e/gql_project_test.go
still shows calls like:
projects(t, ctx, r, 20, wID, "[wID]project", "ALIAS1", true)
- And similar invocations with a hardcoded count (e.g., 10)
Verify that an additional project (or related change) has been intentionally introduced so that expecting 21 projects (and the corresponding count/edge changes on lines 617 and 620) is correct. If the intention is to adjust the test expectations, please confirm all parts of the test—in both data setup and assertions—are updated accordingly.
42-42
:✅ Verification successful
Verify the test expectation change.
The test expectation for the number of edges has been increased from 1 to 2. Ensure this change aligns with the actual data setup in the test.
🏁 Script executed:
#!/bin/bash # Description: Check the test data setup to verify the expected number of edges. # Search for project creation calls in the test rg -A 5 "createProject\(" server/e2e/gql_project_test.goLength of output: 2566
🏁 Script executed:
#!/bin/bash # Display lines around the assertion at line 42 to provide additional context. sed -n '40,44p' server/e2e/gql_project_test.goLength of output: 181
Test Expectation Change Verified
The assertion at line 42 now checks that
edges.Length()
equals 2, which is consistent with the test data setup. In this test, only two projects are marked (via the starred calls) to be included in the query results, so the updated expectation appropriately reflects the changes. Please ensure that any future modifications to the test data or filtering logic also consider this expected count.server/internal/usecase/interactor/storytelling.go (1)
188-194
: LGTM! Google Analytics support added correctly.The implementation adds support for enabling/disabling Google Analytics and setting the tracking ID. The changes are well-structured and maintain consistency with the existing codebase.
server/internal/usecase/interactor/nlslayer.go (2)
859-864
: LGTM! Improved feature creation with explicit ID.The change uses
NewFeature
with an explicit ID creation, which is a better approach than the previous implementation.
1109-1134
: LGTM! Enhanced feature collection handling.The changes improve the handling of feature collections during import by:
- Using clearer variable names
- Properly handling feature properties
- Maintaining better code structure
server/gql/scene.graphql (1)
50-50
: LGTM! Improved formatting.The change adds a space after the
Query
keyword, improving readability while maintaining functionality.server/gql/asset.graphql (6)
5-5
: LGTM! TheprojectId
field is correctly added to the Asset type.The optional
projectId
field allows assets to be associated with projects, enhancing asset organization.
24-24
: LGTM! TheprojectId
field is correctly added to the CreateAssetInput.The optional
projectId
field in the input type maintains consistency with the Asset type.
29-32
: LGTM! The UpdateAssetInput type is well-defined.The input type correctly includes the required
assetId
and optionalprojectId
fields.
49-52
: LGTM! The UpdateAssetPayload type is well-structured.The payload type correctly mirrors the input fields, providing a consistent response structure.
75-75
: LGTM! TheprojectId
parameter is correctly added to the assets query.The optional parameter allows filtering assets by project.
84-84
: LGTM! The updateAsset mutation is correctly defined.The mutation is properly integrated into the schema with appropriate input and payload types.
server/gql/workspace.graphql (3)
8-14
: LGTM! The assets method signature is well-structured.The method now includes the optional
projectId
parameter and has improved readability with parameters on separate lines.
15-21
: LGTM! The projects method signature is well-formatted.The method's parameters are now properly formatted on separate lines, improving readability.
121-123
: LGTM! The removeMemberFromTeam mutation is well-formatted.The mutation's input parameter is now properly formatted on a separate line.
server/gql/storytelling.graphql (2)
23-24
: LGTM! Google Analytics fields are correctly added to the Story type.The required fields
enableGa
andtrackingId
are properly defined with appropriate types.
86-89
:❓ Verification inconclusive
LGTM! Google Analytics fields are correctly added to UpdateStoryInput.
The optional fields
enableGa
andtrackingId
are properly defined under a clear comment section.Let's verify that the tracking ID format is valid when provided:
🏁 Script executed:
#!/bin/bash # Search for validation of Google Analytics tracking ID format rg -A 5 'trackingId.*String'Length of output: 25485
Google Analytics Fields Update – Review & Suggestion
The GraphQL schema in
server/gql/storytelling.graphql
correctly adds the optionalenableGa
andtrackingId
fields under a clear comment. A repository-wide search confirms that these fields are uniformly declared asString
(orString!
in some contexts) without any additional format validation logic fortrackingId
.
- The tracking ID is defined simply as a string in multiple locations (e.g., in
storytelling.graphql
,project.graphql
, and generated Go files) without regex-based or similar validations.- If enforcing a specific Google Analytics tracking ID format (e.g., "UA-XXXXXX-X") is desired, please consider implementing the appropriate validation either in the schema or within the resolver/input processing logic.
Overall, the changes are LGTM, with a follow-up to verify whether additional format validation is necessary.
server/go.mod (1)
133-133
: LGTM! The dependency is correctly marked as indirect.The
github.com/oklog/ulid
package is appropriately marked as an indirect dependency.server/internal/adapter/gql/resolver_mutation_storytelling.go (1)
58-59
: LGTM! Google Analytics fields added correctly.The addition of
EnableGa
andTrackingID
fields to theUpdateStoryInput
aligns with the PR objectives and follows the existing code structure.server/internal/usecase/interactor/project.go (1)
627-649
: LGTM! Well-structured helper function.The
AddZipAsset
function is well-implemented with proper error handling and resource cleanup. The function:
- Handles external URLs gracefully
- Uses defer for proper cleanup
- Has clear error messages
7c10081
to
ddf2e3c
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 (4)
server/internal/usecase/interactor/asset.go (1)
108-127
:⚠️ Potential issueValidate project access permissions in Update method.
The Update method should verify that the operator has permission to associate the asset with the new project.
Apply this diff to add project access validation:
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 } + // Validate project access + if pid != nil { + project, err := i.repos.Project.FindByID(ctx, *pid) + if err != nil { + return aid, pid, err + } + if project == nil || project.Workspace() != asset.Workspace() { + return aid, pid, interfaces.ErrOperationDenied + } + if ok := operator.IsWritableWorkspace(project.Workspace()); !ok { + return aid, pid, interfaces.ErrOperationDenied + } + } + asset.SetProject(pid) return aid, pid, i.repos.Asset.Save(ctx, asset) }, ) }server/e2e/common.go (1)
266-271
: 🛠️ Refactor suggestionAdd error handling for type assertion in JSONEqRegexpValue.
The type assertion should handle failure cases more gracefully.
Apply this diff to improve error handling:
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 + actualData, ok := actual.Raw().(map[string]interface{}) + if !ok { + t.Errorf("expected map[string]interface{}, got %T", actual.Raw()) + return false + } + return JSONEqRegexpInterface(t, actualData, expected) }server/pkg/storytelling/story.go (2)
134-136
: 🛠️ Refactor suggestionAdd validation for enableGa and trackingID combination.
When
enableGa
is true,trackingID
should be required to ensure proper Google Analytics configuration.func (s *Story) SetEnableGa(enableGa bool) error { + if enableGa && s.trackingID == "" { + return fmt.Errorf("tracking ID is required when Google Analytics is enabled") + } s.enableGa = enableGa + return nil }
138-140
: 🛠️ Refactor suggestionAdd validation for Google Analytics tracking ID format.
The tracking ID should follow Google Analytics format (UA-XXXXX-Y for Universal Analytics or G-XXXXXX for GA4).
func (s *Story) SetTrackingID(trackingID string) error { + // Validate GA4 (G-XXXXXX) or Universal Analytics (UA-XXXXX-Y) format + if trackingID != "" && !regexp.MustCompile(`^(UA-\d{4,10}-\d{1,4}|G-[A-Z0-9]+)$`).MatchString(trackingID) { + return fmt.Errorf("invalid Google Analytics tracking ID format") + } s.trackingID = trackingID + return nil }
🧹 Nitpick comments (12)
server/pkg/scene/builder/builder.go (1)
128-163
: Enhance error messages for better debugging.While the error messages have been improved, they could be more descriptive by including the actual error details.
Apply 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)This change:
- Uses
fmt.Errorf
for better error wrapping- Improves error message formatting
- Uses lowercase for error messages as per Go conventions
- Preserves error chain using
%w
verbserver/e2e/gql_storytelling_test.go (1)
869-904
: Consider simplifying the function signature.The
removeBlock
function returns a string that isn't used in the test assertions. Consider simplifying the return signature to just(GraphQLRequest, *httpexpect.Value)
for better maintainability.-func removeBlock(e *httpexpect.Expect, storyID, pageID, blockID string) (GraphQLRequest, *httpexpect.Value, string) { +func removeBlock(e *httpexpect.Expect, storyID, pageID, blockID string) (GraphQLRequest, *httpexpect.Value) { // ... function body ... - return requestBody, res, res.Path("$.data.removeStoryBlock.blockId").Raw().(string) + return requestBody, res }server/internal/adapter/gql/gqlmodel/convert_project.go (3)
108-113
: Add documentation for the new type.The
ProjectExport
type is well-structured, but would benefit from documentation explaining its purpose and usage.Add a documentation comment:
+// ProjectExport represents the essential project data for export/import operations. +// It contains a subset of Project fields that are relevant for project templates. type ProjectExport struct {
115-125
: Add documentation for the conversion function.The
ToProjectExport
function is well-implemented and follows the established conversion patterns in the codebase.Add a documentation comment:
+// ToProjectExport converts a project.Project domain model to a ProjectExport model. +// Returns nil if the input project is nil. func ToProjectExport(p *project.Project) *ProjectExport {
127-137
: Optimize JSON conversion for better performance.The current implementation performs unnecessary JSON marshal/unmarshal operations. Consider using direct field mapping or JSON decoder for better performance.
Here's a more efficient implementation:
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { - var p ProjectExport - bytes, err := json.MarshalIndent(data, "", " ") - if err != nil { - return nil - } - if err := json.Unmarshal(bytes, &p); err != nil { - return nil - } - return &p + if data == nil { + return nil + } + + p := &ProjectExport{ + Name: toString(data["name"]), + Description: toString(data["description"]), + } + + if v, ok := data["visualizer"].(string); ok { + p.Visualizer = Visualizer(v) + } + + if v, ok := data["imageUrl"].(string); ok { + if u, err := url.Parse(v); err == nil { + p.ImageURL = u + } + } + + return p } + +func toString(v any) string { + if s, ok := v.(string); ok { + return s + } + return "" +}server/e2e/gql_project_export_import_test.go (3)
58-61
: Consider usingt.Cleanup
for file cleanup.Instead of using a deferred function for cleanup, consider using
t.Cleanup
which is more idiomatic for tests and ensures cleanup even if the test fails.-defer func() { - err := os.Remove(fileName) - assert.Nil(t, err) -}() +t.Cleanup(func() { + err := os.Remove(fileName) + assert.Nil(t, err) +})
103-119
: Add documentation for helper functions.The helper functions
isIgnore
andisID
lack documentation explaining their purpose and expected behavior.Add documentation comments:
+// isIgnore checks if two lines should be ignored in comparison based on specific property names func isIgnore(propertyName, expectedLine, actualLine string) bool { +// isID checks if the given text represents a valid widget ID func isID(text string) bool {
172-186
: Add retry mechanism for flaky GraphQL queries.Based on the retrieved learnings about problems with GraphQL, consider adding a retry mechanism for the
getScene
function.+func getSceneWithRetry(e *httpexpect.Expect, s string, l string, maxRetries int) *httpexpect.Value { + var lastErr error + for i := 0; i < maxRetries; i++ { + if v := getScene(e, s, l); v != nil { + return v + } + time.Sleep(time.Second * time.Duration(i+1)) + } + t.Fatalf("Failed to get scene after %d retries: %v", maxRetries, lastErr) + return nil +}server/internal/adapter/gql/resolver_mutation_asset.go (1)
41-62
: Improve variable naming for consistency.Consider the following improvements:
- Rename
err2
toerr
for consistency with error handling in other methods.- Use consistent variable naming between similar blocks:
pid
vspidValue
.Apply this diff to improve consistency:
- var pid *id.ProjectID + var projectID *id.ProjectID if project := input.ProjectID; project != nil { - pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID) + pid, err := gqlmodel.ToID[id.Project](*input.ProjectID) if err != nil { return nil, err } - pid = &pidValue + projectID = &pid } - a, p, err2 := usecases(ctx).Asset.Update(ctx, aid, pid, getOperator(ctx)) - if err2 != nil { + a, p, err := usecases(ctx).Asset.Update(ctx, aid, projectID, getOperator(ctx)) + if err != nil { return nil, err2 }server/e2e/gql_asset_test.go (1)
124-179
: Add error case tests for project association.The test suite should include error cases such as:
- Attempting to associate an asset with a non-existent project
- Attempting to associate an asset with a project from a different workspace
- Attempting to update an asset without proper permissions
Example test cases to add:
+ // Test error cases + nonExistentPid := "non-existent-project-id" + res = updateAsset(e, assetId0, &nonExistentPid) + res.Path("$.errors").Array().NotEmpty() + + // Create project in different workspace + differentTeamId := createWorkspace(e, "different-team") + differentPid := createProject(e, "different-project", differentTeamId) + res = updateAsset(e, assetId0, &differentPid) + res.Path("$.errors").Array().NotEmpty()server/internal/usecase/interactor/scene.go (2)
711-714
: Consider returning the error instead of just logging it.While the error is logged, silently continuing after a failed asset addition could lead to incomplete exports. Consider either:
- Returning the error to let the caller decide how to handle it
- Adding a comment explaining why it's safe to continue despite the error
- u, _ := url.Parse(urlStr) - if err := AddZipAsset(ctx, i.file, zipWriter, u.Path); err != nil { - log.Infofc(ctx, "Fail nLayer addZipAsset :", err.Error()) - } + u, err := url.Parse(urlStr) + if err != nil { + return nil, nil, fmt.Errorf("invalid URL in nLayer: %w", err) + } + if err := AddZipAsset(ctx, i.file, zipWriter, u.Path); err != nil { + return nil, nil, fmt.Errorf("failed to add nLayer asset: %w", err) + }
740-742
: Consider returning the error instead of just logging it.Similar to the previous comment, consider returning the error instead of just logging it to ensure proper error handling.
- if u, ok := field.Value().Value().(*url.URL); ok { - if err := AddZipAsset(ctx, i.file, zipWriter, u.Path); err != nil { - log.Infofc(ctx, "Fail widget addZipAsset :", err.Error()) - } + if u, ok := field.Value().Value().(*url.URL); ok { + if err := AddZipAsset(ctx, i.file, zipWriter, u.Path); err != nil { + return nil, nil, fmt.Errorf("failed to add widget asset: %w", err) + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
server/e2e/test.zip
is excluded by!**/*.zip
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 (107)
server/Makefile
(3 hunks)server/e2e/common.go
(7 hunks)server/e2e/dataset_export_test.go
(0 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_custom_property_test.go
(1 hunks)server/e2e/gql_layer_test.go
(0 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_nlslayer_test.go
(0 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 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/storytelling.graphql
(3 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_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_plugin.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_scene.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 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
(9 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
(3 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 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/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/layer.go
(0 hunks)server/internal/usecase/interactor/nlslayer.go
(3 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/scene.go
(5 hunks)server/internal/usecase/interactor/scene_test.go
(0 hunks)server/internal/usecase/interactor/storytelling.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 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/nlslayer/feature.go
(0 hunks)server/pkg/nlslayer/feature_test.go
(1 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)server/pkg/scene/builder/story.go
(1 hunks)server/pkg/scene/builder_test.go
(0 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)web/src/services/api/propertyApi/utils.ts
(1 hunks)
⛔ Files not processed due to max files limit (6)
- web/src/services/api/sceneApi.ts
- web/src/services/gql/fragments/dataset.ts
- 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/scene.ts
💤 Files with no reviewable changes (47)
- server/internal/adapter/gql/gqlmodel/convert_plugin.go
- server/pkg/scene/builder_test.go
- server/e2e/gql_nlslayer_test.go
- server/internal/adapter/gql/resolver_property_test.go
- server/pkg/scene/builder/builder_test.go
- server/internal/usecase/interactor/project_test.go
- server/internal/adapter/gql/gqlmodel/convert.go
- server/internal/infrastructure/mongo/mongodoc/scene.go
- server/pkg/nlslayer/feature.go
- server/gql/property.graphql
- server/internal/adapter/gql/gqlmodel/convert_dataset.go
- server/internal/usecase/interactor/scene_test.go
- server/internal/adapter/gql/context.go
- server/internal/adapter/gql/gqlmodel/convert_tag.go
- server/internal/usecase/interactor/layer.go
- server/internal/adapter/gql/resolver_mutation_scene.go
- server/e2e/dataset_export_test.go
- server/internal/adapter/gql/gqlmodel/convert_scene.go
- server/internal/adapter/gql/loader.go
- server/gqlgen.yml
- server/e2e/gql_project_export_test.go
- server/internal/adapter/gql/gqldataloader/dataloader.go
- server/internal/adapter/gql/gqlmodel/models.go
- server/internal/adapter/gql/gqlmodel/convert_layer.go
- server/pkg/scene/builder/encoder_test.go
- server/pkg/layer/layerops/processor_test.go
- server/internal/adapter/gql/resolver_mutation_dataset.go
- server/internal/adapter/gql/resolver_mutation_tag.go
- server/internal/adapter/gql/resolver_scene.go
- server/internal/adapter/gql/resolver_dataset.go
- server/gql/dataset.graphql
- server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
- server/internal/adapter/gql/resolver_dataset_schema.go
- server/internal/adapter/gql/resolver_tag.go
- server/gql/cluster.graphql
- server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
- server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
- server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
- server/internal/adapter/gql/resolver_property.go
- server/e2e/gql_layer_test.go
- server/internal/adapter/gql/gqldataloader/tagloader_gen.go
- server/internal/adapter/gql/loader_tag.go
- server/internal/adapter/gql/loader_dataset.go
- server/internal/adapter/gql/resolver_layer.go
- server/gql/layer.graphql
- server/internal/adapter/gql/resolver_mutation_layer.go
- server/gql/tag.graphql
🚧 Files skipped from review as they are similar to previous changes (37)
- server/e2e/mock_test.go
- server/pkg/scene/builder/story.go
- server/gql/plugin.graphql
- server/internal/adapter/gql/gqlmodel/convert_asset.go
- server/pkg/nlslayer/feature_test.go
- server/internal/infrastructure/fs/file_test.go
- server/Makefile
- server/pkg/asset/builder.go
- server/pkg/scene/builder/encoder.go
- server/internal/infrastructure/fs/file.go
- server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go
- server/e2e/gql_validate_geojson_test.go
- server/pkg/layer/encoding/exporter.go
- server/internal/usecase/interfaces/story.go
- server/e2e/gql_scene_test.go
- server/pkg/asset/asset.go
- server/pkg/storytelling/story_bulider.go
- server/internal/usecase/interactor/storytelling.go
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/gql/scene.graphql
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/go.mod
- server/e2e/gql_user_test.go
- server/internal/app/auth_client.go
- server/pkg/asset/id.go
- server/internal/usecase/interfaces/project.go
- server/internal/usecase/repo/asset.go
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/e2e/gql_project_test.go
- server/internal/app/app.go
- server/gql/storytelling.graphql
- server/internal/infrastructure/mongo/asset.go
- server/gql/asset.graphql
- server/internal/adapter/gql/resolver_team.go
- server/internal/infrastructure/mongo/mongodoc/asset.go
- server/internal/usecase/interfaces/asset.go
- server/gql/workspace.graphql
🧰 Additional context used
🧠 Learnings (2)
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
server/internal/adapter/gql/resolver_mutation_project.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
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: Redirect rules - reearth-web
- GitHub Check: Header rules - reearth-web
- GitHub Check: Pages changed - reearth-web
🔇 Additional comments (34)
server/pkg/scene/builder/scene.go (3)
11-25
: LGTM! The GA fields are properly integrated.The new Google Analytics fields (
EnableGA
andTrackingID
) are correctly added to thesceneJSON
struct with appropriate types and json tags.
27-40
: LGTM! The method changes are well-structured.The
sceneJSON
method has been properly updated to:
- Accept the new GA parameters
- Remove the layers parameter (as mentioned in the AI summary)
- Correctly initialize the scene JSON with GA settings
42-167
: LGTM! Helper methods are well-maintained.The helper methods maintain good separation of concerns and consistent error handling patterns.
server/pkg/scene/builder/builder.go (2)
26-38
: LGTM! Removed unused field.The removal of the unused
encoder
field from theBuilder
struct improves code cleanliness.
181-181
: LGTM! GA parameters correctly propagated.The Google Analytics parameters are properly passed through to the
sceneJSON
method.server/e2e/gql_storytelling_test.go (3)
15-55
: Well-structured test implementation!The test comprehensively covers story CRUD operations and properly validates the new Google Analytics parameters (
enableGa
andtrackingId
).
564-565
: GraphQL queries properly updated for GA support!The changes correctly integrate the new Google Analytics fields (
enableGa
andtrackingId
) into both the UpdateStory mutation and GetScene query.Also applies to: 501-514
232-413
: Comprehensive publishing test implementation!The test thoroughly validates the story publishing functionality, including proper handling of Google Analytics fields in the published output.
server/internal/adapter/gql/gqlmodel/convert_project.go (1)
5-5
: LGTM!The addition of the
net/url
import is necessary for the newProjectExport
type and follows proper import ordering.web/src/services/api/propertyApi/utils.ts (2)
247-248
: Inconsistency with PR objectives.The changes in this file appear unrelated to the PR's stated objective of adding Google Analytics support to the Story component. Please clarify if these changes are intentional or if they were accidentally included in this PR.
Likely an incorrect or invalid review comment.
247-248
:✅ Verification successful
Verify the removal of
actualValue
from merged property fields.The removal of
actualValue
from thePick
type suggests a breaking change in how merged property fields are handled. This change appears to be part of a larger refactoring effort.Run the following script to verify all usages of merged property fields and ensure they don't rely on
actualValue
:
🏁 Script executed:
#!/bin/bash # Description: Search for usages of merged property fields to ensure they don't rely on actualValue # Search for property field usages rg -A 5 'MergedPropertyField' # Search for actualValue references rg 'actualValue'Length of output: 67037
Removal of
actualValue
Verified and ConfirmedThe verification confirms that the
actualValue
property is no longer in use. The grep output shows that the only traces ofactualValue
are in the commented code (with no downstream references relying on it), and the generated GraphQL types have been updated accordingly. Note, however, that the refactoring—including the removal ofactualValue
—appears unrelated to the PR’s stated goal of adding Google Analytics support to the Story component. Please confirm that the additional refactoring changes were intentional and align with your overall objectives.
- File Impact:
web/src/services/api/propertyApi/utils.ts
(changes to the merged property field type)server/e2e/gql_project_export_import_test.go (3)
21-63
: LGTM! Well-structured test with clear steps.The test function is well-organized with clear comments indicating each step of the process. The use of
fullSeeder
provides a more comprehensive test environment.
71-101
: Consider adding test coverage for edge cases incompareValue
.The
compareValue
function handles basic comparison well, but consider adding test coverage for:
- Empty arrays
- Arrays with different lengths
- Objects with different structures
Would you like me to generate test cases for these scenarios?
141-170
:❓ Verification inconclusive
Consider adding validation for imported project data.
The
importProject
function should validate the structure of the imported data before returning.
🏁 Script executed:
#!/bin/bash # Description: Check if the imported project data matches the expected schema # Search for project data validation in the codebase rg -A 5 'type ProjectData struct'Length of output: 35
Action Required: Enhance Validation for Imported Project Data
After examining the current implementation in
server/e2e/gql_project_export_import_test.go
(lines 141–170), it appears that the test only confirms thatprojectData
is not null. Our initial search for a definedProjectData
struct produced no results, which suggests that robust schema validation for the imported project data might be missing.
Current Behavior:
- The function only checks for non-nil data via
projectData.NotNull()
.Recommendation:
- Consider adding validation to explicitly verify that the imported project data includes all expected fields and conforms to a defined schema.
- If a
ProjectData
type or similar schema definition is intended but missing, adding it could improve the test's effectiveness and overall data integrity.Please review these suggestions and implement the necessary validation logic as appropriate.
server/internal/adapter/gql/resolver_query.go (2)
1-225
: Verify alignment with PR objectives.The changes in this file focus on asset handling and are unrelated to the PR's stated objective of adding Google Analytics parameters to the Story component. Please verify if:
- The PR description is accurate
- All relevant files have been included in the review
- These changes should be in a separate PR
15-16
:✅ Verification successful
LGTM! The Assets query now supports project-based filtering.
The addition of the
projectId
parameter allows for more granular asset filtering by project, which is a good improvement to the API.Let's verify that the loader's
FindByWorkspace
method signature matches this change:
🏁 Script executed:
#!/bin/bash # Description: Verify the loader's FindByWorkspace method signature matches the resolver. # Search for the FindByWorkspace method definition in the loader ast-grep --pattern 'func $_(ctx context.Context, teamID gqlmodel.ID, projectId *gqlmodel.ID, $$$) ($_, error)'Length of output: 112
Let's run an additional verification to ensure that the loader's
FindByWorkspace
function signature includes the newprojectId
parameter. Please run the following script to search for the function definition usingrg
:If the output confirms that the function signature includes
projectId *gqlmodel.ID
(along with the other parameters), then the change in the resolver is fully reflected in the loader. Otherwise, please update the loader accordingly.
Please verify that the loader’s
FindByWorkspace
method signature is updated to match the resolver’s call, including theprojectId
parameter.
🏁 Script executed:
[run_scripts] #!/bin/bash # Searching for the FindByWorkspace function definition using rg to verify parameter list. rg -n "func .*FindByWorkspace"Length of output: 2443
LGTM: The Assets query now includes project-based filtering.
The newprojectId
parameter in the resolver is correctly propagated to the loader. The grep output confirms that theFindByWorkspace
method inserver/internal/adapter/gql/loader_asset.go
accepts a*gqlmodel.ID
as its third parameter, which aligns with the updated resolver implementation.
- Location:
server/internal/adapter/gql/loader_asset.go
, line 39server/e2e/gql_me_test.go (1)
8-8
: LGTM!The server initialization has been simplified while maintaining the test functionality.
server/e2e/ping_test.go (1)
9-9
: LGTM!The server initialization has been simplified while maintaining the test functionality.
server/internal/adapter/gql/resolver_mutation_asset.go (1)
19-31
: LGTM!The project ID handling is well-implemented with proper error checking and follows consistent patterns.
server/internal/adapter/gql/loader_asset.go (2)
45-54
: LGTM!The project ID handling is well-implemented with proper error checking and follows consistent patterns.
60-66
: LGTM!The edge creation has been optimized by:
- Preallocating the slice to avoid resizing.
- Simplifying the loop structure.
server/internal/infrastructure/memory/asset.go (1)
49-59
: LGTM! Project filtering logic is well implemented.The new project filtering logic is correctly implemented with proper null checks and maintains backward compatibility when
pid
is nil.server/internal/usecase/interactor/asset.go (1)
39-51
: LGTM! Method signature update is consistent.The method signature update to include project ID is consistent with the repository layer changes.
server/e2e/common.go (1)
283-297
: LGTM! ValueDump function handles different types appropriately.The function now properly handles different types of values and includes a default case for unsupported types.
server/internal/adapter/gql/resolver_mutation_project.go (1)
82-84
: LGTM! GA parameters successfully integrated.The addition of
enableGa
andtrackingId
fields to the project update parameters aligns with the PR objectives to support Google Analytics integration.server/internal/usecase/interactor/project.go (1)
258-264
: LGTM! GA parameters successfully integrated.The addition of
UpdateEnableGA
andUpdateTrackingID
methods properly handles the new Google Analytics parameters, aligning with the PR objectives.server/e2e/gql_custom_property_test.go (1)
347-762
: LGTM! Query structure significantly improved.The GraphQL query has been refactored into a well-structured format with proper fragments, enhancing readability and maintainability. The use of fragments like
PropertyFragment
,PluginFragment
, etc., promotes code reuse.server/internal/usecase/interactor/scene.go (2)
22-22
: LGTM!The import of the storytelling package is necessary for the GA integration changes.
680-683
: LGTM! Improved nil handling.The change adds proper nil and length checks before accessing the story list, preventing potential nil pointer dereferences.
server/internal/usecase/interactor/nlslayer.go (2)
859-861
: LGTM!The change correctly uses the new
NewFeature
API with explicit ID creation.
1109-1134
: LGTM! Improved code readability.The changes enhance the feature collection creation with:
- Better variable names
- Clearer structure
- Consistent use of the new
NewFeature
APIserver/pkg/storytelling/story.go (2)
41-42
: LGTM!The new fields
enableGa
andtrackingID
are well-named and use appropriate types for Google Analytics integration.
57-62
: LGTM!Good addition of the nil check to prevent potential nil pointer dereferences.
server/internal/adapter/gql/gqlmodel/models_gen.go (1)
1221-1222
: LGTM!The GraphQL model changes are consistent with the Story struct changes, correctly exposing the Google Analytics fields in both the Story type and UpdateStoryInput.
Also applies to: 1459-1460
54e2849
to
b36253e
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: 4
♻️ Duplicate comments (7)
server/internal/infrastructure/mongo/asset.go (1)
96-100
:⚠️ Potential issueHandle type assertion failure case.
The type assertion to
bson.M
could silently fail if the assertion is unsuccessful. Consider handling the failure case explicitly.Apply this diff to handle the failure case:
- if andFilter, ok := mongox.And(filter, "url", bson.M{ - "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, - }).(bson.M); ok { - filter = andFilter - } + if andResult := mongox.And(filter, "url", bson.M{ + "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, + }); andResult != nil { + if andFilter, ok := andResult.(bson.M); ok { + filter = andFilter + } + }server/internal/usecase/interactor/asset.go (1)
108-127
:⚠️ Potential issueAdd validation for project access and existence.
The function should verify:
- The target project exists
- The operator has write permissions for the target project's workspace
Apply this diff to improve the implementation:
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, err + } + if project == nil { + return aid, pid, interfaces.ErrNotFound + } + if ok := operator.IsWritableWorkspace(project.Workspace()); !ok { + return aid, pid, interfaces.ErrOperationDenied + } + } asset.SetProject(pid) return aid, pid, i.repos.Asset.Save(ctx, asset) }, ) }server/e2e/common.go (1)
266-271
:⚠️ Potential issueAdd type assertion error handling in JSONEqRegexpValue.
The type assertion discards the error case, which could hide issues.
Add proper error handling:
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 + actualData, ok := actual.Raw().(map[string]interface{}) + if !ok { + t.Errorf("expected map[string]interface{}, got %T", actual.Raw()) + return false + } + return JSONEqRegexpInterface(t, actualData, expected) }server/e2e/gql_asset_test.go (1)
224-240
:⚠️ Potential issueAdd error handling for updateAsset response.
The function should check for errors in the GraphQL response.
Add error handling:
func updateAsset(e *httpexpect.Expect, assetId string, projectId *string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "UpdateAsset", Query: `mutation UpdateAsset($assetId: ID!, $projectId: ID) { updateAsset(input: { assetId: $assetId projectId: $projectId }) { assetId projectId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, "projectId": projectId, }, } - return Request(e, uID.String(), requestBody) + res := Request(e, uID.String(), requestBody) + if errors := res.Path("$.errors").Array(); len(errors.Raw()) > 0 { + return nil + } + return res }server/internal/adapter/gql/resolver_mutation_project.go (2)
212-214
:⚠️ Potential issueHandle potential error during workspace ID conversion.
The workspace ID conversion discards the error, which could lead to runtime issues.
Add error handling:
- workspace, _ := accountdomain.WorkspaceIDFrom(string(input.TeamID)) + workspace, err := accountdomain.WorkspaceIDFrom(string(input.TeamID)) + if err != nil { + return nil, fmt.Errorf("invalid team ID: %w", err) + }
224-224
:⚠️ Potential issueHandle potential error from unmarshalProject.
The unmarshalProject error is discarded, which could lead to nil dereference.
Add error handling:
- projectData, _ := unmarshalProject(tempData) + projectData, err := unmarshalProject(tempData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal project data: %w", err) + }server/e2e/seeder.go (1)
203-222
:⚠️ Potential issueFix incorrect error check conditions.
Multiple error checks use
if err != err
which is logically incorrect and will never evaluate to true. This causes the error handling code to be unreachable.Fix all instances by replacing with
if err != nil
:- if err = r.Scene.Save(ctx, s); err != err { + if err = r.Scene.Save(ctx, s); err != nil { return err } - if err = r.Property.Save(ctx, prop); err != err { + if err = r.Property.Save(ctx, prop); err != nil { return err } - if err = r.Layer.Save(ctx, rootLayer); err != err { + if err = r.Layer.Save(ctx, rootLayer); err != nil { return err } - if err = addWidget(ctx, s, r); err != err { + if err = addWidget(ctx, s, r); err != nil { return err } - if err = addStory(ctx, r); err != err { + if err = addStory(ctx, r); err != nil { return err } - if err = addLayerSimple(ctx, r); err != err { + if err = addLayerSimple(ctx, r); err != nil { return err }
🧹 Nitpick comments (16)
server/internal/usecase/interactor/storytelling_test.go (1)
25-196
: Add test cases for enabled Google Analytics scenarios.The current test only verifies the default values (
enableGa: false
and emptytrackingId
). Consider adding test cases to verify:
- Story import with GA enabled (
enableGa: true
)- Story import with a valid tracking ID
- Story import with both GA enabled and tracking ID set
This will ensure the GA functionality is thoroughly tested.
Here's a suggested test case to add:
func TestImportStoryWithGA(t *testing.T) { // ... (similar setup as TestImportStory) var sceneData map[string]interface{} err := json.Unmarshal([]byte(fmt.Sprintf(`{ "schemaVersion": 1, "id": "%s", "story": { "id": "01j7g9ddvkarms2gmc59ysw66r", "property": {}, "pages": [], "enableGa": true, "trackingId": "UA-12345678-9" } }`, scene.ID())), &sceneData) assert.NoError(t, err) result, err := ifs.ImportStory(ctx, scene.ID(), sceneData, map[string]idx.ID[id.NLSLayer]{}) assert.NoError(t, err) assert.NotNil(t, result) assert.True(t, result.EnableGa()) assert.Equal(t, "UA-12345678-9", result.TrackingId()) }server/internal/usecase/interactor/property_test.go (1)
22-23
: LGTM! Scene initialization simplified.The removal of root layer specification simplifies scene creation without affecting the property tests' functionality.
Consider adding edge cases to improve test coverage.
The current tests focus on happy paths. Consider adding test cases for:
- Invalid property values
- Non-existent properties
- Boundary conditions for list operations
Also applies to: 74-75, 119-120
server/internal/usecase/interactor/nlslayer_test.go (1)
34-35
: LGTM! Scene initialization simplified.The removal of root layer specification simplifies scene creation without affecting the NLS layer tests' functionality.
Consider improving URL handling in tests.
The tests use hardcoded URLs like "https://example.com" and "http://localhost:8080". Consider:
- Using environment variables for URLs
- Adding test cases for invalid URLs
- Mocking URL responses for more robust testing
Also applies to: 85-86, 142-143, 221-222, 289-290
server/internal/adapter/gql/gqlmodel/models_gen.go (2)
138-138
: Ensure references to optionally associated projects are validated.
AddingProjectID
here is potentially useful for linking assets to projects, but please verify that all references are updated accordingly in the resolvers or services. If the asset is meant to always have a project, consider making this field mandatory instead of optional.
581-588
: Validate dynamic typing for 'Value'.
TheValue any
approach can hamper type safety. If fully dynamic values are intentional, ensure runtime checks or validations prevent inadvertent type errors. Also confirm thatSchemaID
andFieldID
correctly reference valid schema entities.server/e2e/gql_scene_test.go (1)
77-100
: Enhance thecreateProjectWithExternalImage
function.While the function is well-structured, consider these improvements:
- Add error handling for failed requests.
- Parameterize hard-coded values like description and imageUrl.
Here's a suggested improvement:
func createProjectWithExternalImage(e *httpexpect.Expect, name string) string { + // Add more parameters for flexibility + func createProjectWithExternalImage(e *httpexpect.Expect, name, description, imageUrl string) (string, error) { requestBody := GraphQLRequest{ OperationName: "CreateProject", Query: `mutation CreateProject($teamId: ID!, $visualizer: Visualizer!, $name: String!, $description: String!, $imageUrl: URL, $coreSupport: Boolean) { createProject( input: {teamId: $teamId, visualizer: $visualizer, name: $name, description: $description, imageUrl: $imageUrl, coreSupport: $coreSupport} ) { project { id + success + error __typename } __typename } }`, Variables: map[string]any{ "name": name, - "description": "abc", - "imageUrl": "https://test.com/project.jpg", + "description": description, + "imageUrl": imageUrl, "teamId": wID.String(), "visualizer": "CESIUM", "coreSupport": true, }, } res := Request(e, uID.String(), requestBody) - return res.Path("$.data.createProject.project.id").Raw().(string) + + // Add error handling + if err := res.Path("$.data.createProject.project.error").Raw(); err != nil { + return "", fmt.Errorf("failed to create project: %v", err) + } + + return res.Path("$.data.createProject.project.id").Raw().(string), nil }server/internal/adapter/gql/resolver_mutation_scene.go (1)
20-20
: Document the purpose of the boolean parameter.The purpose of the
false
parameter inusecases(ctx).Scene.Create
is not clear. Please add a comment explaining its significance.server/e2e/gql_validate_geojson_test.go (2)
201-236
: Add timeout handling for external URL tests.The test cases for external URLs could potentially hang if the remote server is slow to respond or unreachable.
Add a timeout context to the test:
func TestValidateGeoJsonExternal(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() tests := []struct { name string url string hasError bool }{
238-313
: Enhance test coverage for GeoJSON validation.The test cases only cover Point geometry. Consider adding test cases for other geometry types like LineString, Polygon, etc.
Add more test cases:
tests := []struct { name string geometry map[string]any hasError bool }{ { name: "Valid Point", geometry: map[string]any{ "type": "Point", "coordinates": []float64{125.6, 10.1}, }, hasError: false, }, + { + name: "Valid LineString", + geometry: map[string]any{ + "type": "LineString", + "coordinates": [][]float64{{125.6, 10.1}, {125.7, 10.2}}, + }, + hasError: false, + }, + { + name: "Valid Polygon", + geometry: map[string]any{ + "type": "Polygon", + "coordinates": [][][]float64{{{125.6, 10.1}, {125.7, 10.2}, {125.8, 10.3}, {125.6, 10.1}}}, + }, + hasError: false, + }, // ... existing test casesserver/e2e/common.go (1)
35-43
: Consider making disabledAuthConfig immutable.The configuration object is shared across tests and could potentially be modified.
Make the config immutable:
-var ( +var fr *gateway.File + +var disabledAuthConfig = func() *config.Config { + return &config.Config{ + Origins: []string{"https://example.com"}, + AuthSrv: config.AuthSrvConfig{ + Disabled: true, + }, + } +}()server/internal/adapter/gql/resolver_mutation_project.go (1)
338-386
: Add input validation in helper functions.The helper functions don't validate input data before processing.
Add input validation:
func unmarshalProject(data []byte) (map[string]interface{}, error) { + if len(data) == 0 { + return nil, errors.New("empty data") + } var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { return nil, err } projectData, _ := jsonData["project"].(map[string]interface{}) + if projectData == nil { + return nil, errors.New("project data not found") + } return projectData, nil }server/e2e/gql_project_export_import_test.go (2)
59-62
: Add error handling for file cleanup.The deferred cleanup should handle errors more gracefully.
defer func() { - err := os.Remove(fileName) - assert.Nil(t, err) + if err := os.Remove(fileName); err != nil { + t.Logf("Warning: Failed to remove temporary file %s: %v", fileName, err) + } }()
72-102
: Add validation for empty or nil values in compareValue.The function should validate input parameters and handle edge cases.
func compareValue(t *testing.T, key string, e, a *httpexpect.Value) { + if e == nil || a == nil { + t.Fatalf("compareValue: expected and actual values must not be nil") + return + } + if key == "" { + t.Fatalf("compareValue: key must not be empty") + return + } + expected := convertLine(t, key, e) actual := convertLine(t, key, a)server/internal/usecase/interactor/project.go (1)
513-533
: Improve error messages in ExportProjectData.The error messages could be more descriptive and include context.
prj, err := i.projectRepo.FindByID(ctx, projectID) if err != nil { - return nil, errors.New("project " + err.Error()) + return nil, fmt.Errorf("failed to find project %s: %w", projectID, err) } if prj.IsDeleted() { - fmt.Printf("Error Deleted project: %v\n", prj.ID()) - return nil, errors.New("This project is deleted") + return nil, fmt.Errorf("cannot export deleted project: %s", prj.ID()) }server/e2e/gql_custom_property_test.go (1)
347-762
: Consider moving the GraphQL query to a separate file.The query is well-structured but quite long. Moving it to a separate file would improve maintainability and reusability.
- Create a new file
server/e2e/queries/get_scene.graphql
with the query content.- Update the code to read the query from the file:
func getNewLayersOfScene(e *httpexpect.Expect, sId string) *httpexpect.Object { + query, err := os.ReadFile("queries/get_scene.graphql") + if err != nil { + panic(fmt.Errorf("failed to read GraphQL query: %w", err)) + } requestBody := GraphQLRequest{ OperationName: "GetScene", - Query: `query GetScene($sceneId: ID!, $lang: Lang) { - ... - }`, + Query: string(query), Variables: map[string]any{ "sceneId": sId, "lang": "en", }, }server/internal/usecase/interactor/scene.go (1)
100-159
: Enhance property saving with import-specific filtering.The new
isImportProject
parameter controls whether to apply scene filtering during property saving, improving security and access control for imported projects.Consider adding a comment explaining why imported projects require filtered property saving for better maintainability.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
server/e2e/test.zip
is excluded by!**/*.zip
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 (107)
server/Makefile
(3 hunks)server/e2e/common.go
(7 hunks)server/e2e/dataset_export_test.go
(0 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_custom_property_test.go
(1 hunks)server/e2e/gql_layer_test.go
(0 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_nlslayer_test.go
(0 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(2 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 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/storytelling.graphql
(3 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_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_plugin.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_scene.go
(0 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 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
(9 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
(3 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(1 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 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/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/layer.go
(1 hunks)server/internal/usecase/interactor/layer_test.go
(1 hunks)server/internal/usecase/interactor/nlslayer.go
(7 hunks)server/internal/usecase/interactor/nlslayer_test.go
(5 hunks)server/internal/usecase/interactor/plugin_test.go
(1 hunks)server/internal/usecase/interactor/plugin_upload_test.go
(5 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/property_test.go
(3 hunks)server/internal/usecase/interactor/scene.go
(7 hunks)server/internal/usecase/interactor/scene_plugin.go
(0 hunks)server/internal/usecase/interactor/scene_plugin_test.go
(3 hunks)server/internal/usecase/interactor/scene_test.go
(1 hunks)server/internal/usecase/interactor/storytelling.go
(1 hunks)server/internal/usecase/interactor/storytelling_test.go
(2 hunks)server/internal/usecase/interactor/style_test.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/scene.go
(1 hunks)server/internal/usecase/interfaces/story.go
(1 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/initializer_test.go
(0 hunks)server/pkg/layer/layerops/processor_test.go
(0 hunks)server/pkg/nlslayer/feature.go
(0 hunks)
⛔ Files not processed due to max files limit (21)
- server/pkg/nlslayer/feature_test.go
- server/pkg/scene/builder.go
- server/pkg/scene/builder/builder.go
- server/pkg/scene/builder/builder_test.go
- server/pkg/scene/builder/encoder.go
- server/pkg/scene/builder/encoder_test.go
- server/pkg/scene/builder/scene.go
- server/pkg/scene/builder/story.go
- server/pkg/scene/builder_test.go
- server/pkg/scene/scene.go
- server/pkg/scene/scene_test.go
- server/pkg/scene/sceneops/plugin_migrator_test.go
- server/pkg/storytelling/story.go
- server/pkg/storytelling/story_bulider.go
- web/src/services/api/propertyApi/utils.ts
- web/src/services/api/sceneApi.ts
- web/src/services/gql/fragments/dataset.ts
- 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/scene.ts
💤 Files with no reviewable changes (43)
- server/internal/adapter/gql/gqlmodel/convert_plugin.go
- server/internal/adapter/gql/resolver_property_test.go
- server/internal/usecase/interactor/scene_plugin.go
- server/pkg/nlslayer/feature.go
- server/internal/adapter/gql/context.go
- server/internal/adapter/gql/gqlmodel/models.go
- server/pkg/layer/initializer_test.go
- server/internal/usecase/interactor/project_test.go
- server/e2e/gql_nlslayer_test.go
- server/internal/adapter/gql/gqlmodel/convert.go
- server/internal/adapter/gql/loader.go
- server/gql/property.graphql
- server/internal/adapter/gql/gqlmodel/convert_dataset.go
- server/e2e/dataset_export_test.go
- server/e2e/gql_project_export_test.go
- server/internal/adapter/gql/gqldataloader/dataloader.go
- server/pkg/layer/layerops/processor_test.go
- server/e2e/gql_layer_test.go
- server/internal/adapter/gql/resolver_layer.go
- server/gqlgen.yml
- server/internal/adapter/gql/gqlmodel/convert_tag.go
- server/internal/adapter/gql/gqlmodel/convert_layer.go
- server/internal/adapter/gql/gqlmodel/convert_scene.go
- server/internal/adapter/gql/resolver_scene.go
- server/internal/infrastructure/mongo/mongodoc/scene.go
- server/internal/adapter/gql/resolver_dataset.go
- server/gql/cluster.graphql
- server/internal/adapter/gql/resolver_mutation_tag.go
- server/internal/adapter/gql/resolver_mutation_dataset.go
- server/gql/dataset.graphql
- server/internal/adapter/gql/gqldataloader/tagitemloader_gen.go
- server/gql/tag.graphql
- server/internal/adapter/gql/gqldataloader/taggrouploader_gen.go
- server/internal/adapter/gql/gqldataloader/datasetschemaloader_gen.go
- server/internal/adapter/gql/resolver_dataset_schema.go
- server/internal/adapter/gql/resolver_tag.go
- server/internal/adapter/gql/resolver_property.go
- server/internal/adapter/gql/resolver_mutation_layer.go
- server/internal/adapter/gql/gqldataloader/tagloader_gen.go
- server/internal/adapter/gql/loader_tag.go
- server/internal/adapter/gql/gqldataloader/datasetloader_gen.go
- server/gql/layer.graphql
- server/internal/adapter/gql/loader_dataset.go
🚧 Files skipped from review as they are similar to previous changes (33)
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/e2e/mock_test.go
- server/pkg/asset/builder.go
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/Makefile
- server/pkg/layer/encoding/exporter.go
- server/internal/usecase/interfaces/story.go
- server/internal/usecase/interactor/scene_test.go
- server/gql/plugin.graphql
- server/go.mod
- server/internal/infrastructure/fs/file_test.go
- server/e2e/gql_user_test.go
- server/internal/infrastructure/fs/file.go
- server/internal/app/app.go
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/pkg/asset/id.go
- server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go
- server/internal/adapter/gql/loader_asset.go
- server/pkg/asset/asset.go
- server/gql/storytelling.graphql
- server/internal/infrastructure/mongo/mongodoc/asset.go
- server/gql/asset.graphql
- server/internal/adapter/gql/gqlmodel/convert_asset.go
- server/gql/workspace.graphql
- server/internal/infrastructure/memory/asset.go
- server/internal/usecase/interfaces/asset.go
- server/internal/app/auth_client.go
- server/internal/usecase/interfaces/project.go
- server/internal/adapter/gql/resolver_mutation_asset.go
- server/internal/usecase/repo/asset.go
- server/internal/adapter/gql/resolver_team.go
- server/internal/usecase/interactor/nlslayer.go
- server/internal/usecase/interactor/storytelling.go
🧰 Additional context used
🧠 Learnings (2)
server/internal/adapter/gql/resolver_mutation_project.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
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`.
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: ci-server / ci-server-lint
🔇 Additional comments (35)
server/internal/infrastructure/mongo/asset.go (2)
67-67
: LGTM! Method signature updated to support project-based filtering.The method signature change is well-structured, making the project ID parameter optional through the pointer type.
72-80
: LGTM! Clear and efficient filter logic implementation.The filter logic elegantly handles both project-specific and team-wide asset filtering, maintaining backward compatibility while adding new functionality.
server/internal/usecase/interactor/storytelling_test.go (2)
35-35
: LGTM! Scene initialization looks good.The scene initialization correctly includes the workspace and project IDs, and the removal of RootLayer is consistent with similar changes across other test files.
164-164
: LGTM! GA fields added as per requirements.The new fields
enableGa
andtrackingId
have been added with appropriate default values.Also applies to: 187-187
server/internal/usecase/interactor/layer_test.go (1)
19-20
: LGTM! Scene initialization simplified.The removal of root layer specification simplifies scene creation without affecting the test's functionality, as the scene object is only used for ID reference in the infobox creation test.
server/internal/usecase/interactor/style_test.go (1)
29-30
: LGTM! Scene initialization simplified.The removal of root layer specification simplifies scene creation without affecting the style import test's functionality, as the scene object is only used for ID reference in the JSON data.
server/internal/usecase/interactor/scene_plugin_test.go (1)
101-101
: LGTM! Scene initialization simplified.The removal of root layer from scene initialization aligns with the broader refactoring effort while maintaining test functionality.
server/internal/usecase/interactor/plugin_upload_test.go (1)
98-98
: LGTM! Scene initialization simplified.The removal of root layer from scene initialization aligns with the broader refactoring effort while maintaining test functionality.
server/internal/usecase/interactor/layer.go (1)
1062-1062
:❓ Verification inconclusive
Verify the impact of returning nil layers.
The ImportLayer function now returns nil for the layer list instead of the actual imported layers. This change could affect callers that expect a non-nil layer list.
Run the following script to find potential callers that might be affected:
🏁 Script executed:
#!/bin/bash # Search for calls to ImportLayer to verify handling of returned layers rg -A 5 'ImportLayer\('Length of output: 1017
Action Required: Verify Nil Layer List Handling in ImportLayer
The updated ImportLayer function now deliberately returns
nil
for the layer list (first return value) instead of an actual list. Our search indicates that the function is defined in both the interactor and its interface without any obvious caller-side checks against a nil layer list. This change may lead to unintended nil dereferences or logic errors in parts of the code that assume a valid, non-nil layer list.
- Ensure all callers invoking ImportLayer are prepared to handle a nil layer list.
- Verify that downstream operations (e.g., iterating over the list or checking its length) do not introduce bugs.
- Update documentation if returning nil is an intentional design choice, or consider returning an empty list if that better meets caller expectations.
server/internal/usecase/interactor/plugin_test.go (1)
33-33
: LGTM! Scene initialization simplified.The removal of root layer from scene initialization aligns with the broader refactoring effort while maintaining test functionality.
server/internal/adapter/gql/gqlmodel/models_gen.go (8)
186-186
: Confirm optional vs mandatory project association.
InCreateAssetInput
, the newProjectID
field is nullable. Confirm that business logic and validations handle the scenario where no project is specified. Otherwise, consider making the project association required.
906-915
: Potential inconsistency with theID
field type.
Here,ID
is defined as a standardstring
, whereas other similar fields rely on the customID
alias. Confirm this distinction is intentional; otherwise, unify the types for consistency and clarity.
919-921
: Check necessity of dataset references.
These fields reference datasets, yet related dataset code was removed in other parts of the codebase. Verify these definitions are still relevant or remove them if they are no longer needed.
1221-1222
: New GA tracking fields in Story.
AddingenableGa
andtrackingId
to theStory
type appears correct. Ensure you have tests to confirm these fields function as expected and that front-end updates align with these new fields.
1332-1334
: Introducing project linkage to asset updates.
The newProjectID
withinUpdateAssetInput
is consistent with the overall design. Double-check the resolver logic to handle optional vs. required project references properly.
1337-1339
: Refined UpdateAssetPayload fields.
Returning bothAssetID
andProjectID
in the payload cleanly communicates the updated asset state. The design looks good.
1459-1460
: Optional GA fields in update requests.
MakingEnableGa
andTrackingID
pointers inUpdateStoryInput
is a suitable approach for partial updates. Confirm you correctly distinguish between “no update requested” vs. “explicitly clear the field.”
1848-1848
: Extended case statement for plugin extension types.
The updated case line appears to include additional plugin extension types. This is fine as long as the switch statement’s logic is consistent with the new extension coverage.server/e2e/gql_me_test.go (1)
8-8
: LGTM! Server initialization has been simplified.The change from
StartServer
toServer
streamlines the test setup while maintaining the test's functionality.server/e2e/ping_test.go (1)
9-9
: LGTM! Server initialization has been simplified.The change from
StartServer
toServerPingTest
maintains the test's functionality while making the setup more concise.server/internal/usecase/interfaces/scene.go (1)
25-25
: Document the purpose of the new boolean parameter.The
Create
method signature has been updated to include a boolean parameter, but its purpose is not documented. Please add documentation to clarify the parameter's role and expected behavior.server/e2e/gql_scene_test.go (1)
16-16
: LGTM! Response object access is more explicit.The changes to use
Object().Value()
instead of directValue()
calls improve type safety and make the object traversal more explicit.Also applies to: 42-42, 71-73
server/internal/adapter/gql/gqlmodel/convert_project.go (2)
108-113
: LGTM!The
ProjectExport
struct is well-designed with appropriate field types and JSON tags.
115-125
: LGTM!The function correctly handles nil input and properly maps all required fields.
server/internal/usecase/interactor/asset.go (3)
39-51
: LGTM!The function correctly handles permissions and passes all parameters to the repository layer.
91-91
: LGTM!The function correctly sets the project ID during asset creation.
154-195
: LGTM!The function correctly handles file imports and sets all required fields, including the project ID.
server/e2e/gql_project_export_import_test.go (1)
21-64
: LGTM! Well-structured test with clear steps.The test is organized into clear steps with descriptive comments, making it easy to understand and maintain. The test also properly cleans up by removing the exported file.
server/e2e/gql_project_test.go (2)
42-42
: LGTM: Test expectation updated to match new data structure.The test now expects 2 edges instead of 1, which aligns with the changes in the test data setup.
144-144
: LGTM: Test expectation updated to match new data structure.The test now expects 5 edges instead of 4, which aligns with the changes in the test data setup.
server/internal/usecase/interactor/scene.go (2)
674-677
: Fix potential nil pointer dereference.The code now safely handles the case where
storyList
is nil or empty, preventing potential runtime panics.
705-708
: Improve error handling in asset export.The code now:
- Properly handles URL parsing
- Uses
AddZipAsset
for consistent asset handling- Logs errors without failing the export
server/e2e/gql_storytelling_test.go (2)
15-55
: LGTM: Comprehensive test coverage for Story CRUD operations.The test suite properly validates:
- Story creation and retrieval
- Story updates including GA parameters
- Story deletion
561-583
:✅ Verification successful
Verify GA parameters in story update mutation.
The mutation now includes
enableGa
andtrackingId
parameters, aligning with the PR objectives.Run this script to ensure all story-related queries include the new GA fields:
🏁 Script executed:
#!/bin/bash # Search for story-related GraphQL queries and verify GA fields rg -l 'type Story' | xargs rg 'enableGa|trackingId'Length of output: 30610
GA Parameters in Story Update Mutation Verified
The mutation in
server/e2e/gql_storytelling_test.go
now correctly includes theenableGa
andtrackingId
fields. I ran a script to search for these parameters across the GraphQL models and generated code. The results confirm that both fields are present in all relevant story-related queries and mutations (e.g. inserver/internal/adapter/gql/gqlmodel/models_gen.go
,server/internal/adapter/gql/generated.go
, andserver/gql/storytelling.graphql
).No further changes are needed on this part of the code.
server/gql/scene.graphql (1)
50-52
: LGTM: Improved schema formatting.The added space after
Query
keyword improves readability and maintains consistent formatting.
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { | ||
var p ProjectExport | ||
bytes, err := json.MarshalIndent(data, "", " ") | ||
if err != nil { | ||
return nil | ||
} | ||
if err := json.Unmarshal(bytes, &p); err != nil { | ||
return nil | ||
} | ||
return &p | ||
} |
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
Consider improving error handling and JSON conversion.
- Using
json.MarshalIndent
for conversion is unnecessary and less efficient thanjson.Marshal
. - Silently returning nil on errors makes debugging harder.
Apply this diff to improve the implementation:
func ToProjectExportFromJSON(data map[string]any) *ProjectExport {
var p ProjectExport
- bytes, err := json.MarshalIndent(data, "", " ")
+ bytes, err := json.Marshal(data)
if err != nil {
- return nil
+ return nil // Consider logging the error or returning it
}
if err := json.Unmarshal(bytes, &p); err != nil {
- return nil
+ return nil // Consider logging the error or returning it
}
return &p
}
📝 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 ToProjectExportFromJSON(data map[string]any) *ProjectExport { | |
var p ProjectExport | |
bytes, err := json.MarshalIndent(data, "", " ") | |
if err != nil { | |
return nil | |
} | |
if err := json.Unmarshal(bytes, &p); err != nil { | |
return nil | |
} | |
return &p | |
} | |
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { | |
var p ProjectExport | |
bytes, err := json.Marshal(data) | |
if err != nil { | |
return nil // Consider logging the error or returning it | |
} | |
if err := json.Unmarshal(bytes, &p); err != nil { | |
return nil // Consider logging the error or returning it | |
} | |
return &p | |
} |
func (r *queryResolver) Assets(ctx context.Context, teamID gqlmodel.ID, projectId *gqlmodel.ID, pagination *gqlmodel.Pagination, keyword *string, sortType *gqlmodel.AssetSort) (*gqlmodel.AssetConnection, error) { | ||
return loaders(ctx).Asset.FindByWorkspace(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination) |
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.
Fix inconsistent function name.
The loader call uses FindByWorkspace
but the implementation uses FindByWorkspaceProject
.
Apply this diff to fix the inconsistency:
- return loaders(ctx).Asset.FindByWorkspace(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination)
+ return loaders(ctx).Asset.FindByWorkspaceProject(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination)
📝 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 (r *queryResolver) Assets(ctx context.Context, teamID gqlmodel.ID, projectId *gqlmodel.ID, pagination *gqlmodel.Pagination, keyword *string, sortType *gqlmodel.AssetSort) (*gqlmodel.AssetConnection, error) { | |
return loaders(ctx).Asset.FindByWorkspace(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination) | |
func (r *queryResolver) Assets(ctx context.Context, teamID gqlmodel.ID, projectId *gqlmodel.ID, pagination *gqlmodel.Pagination, keyword *string, sortType *gqlmodel.AssetSort) (*gqlmodel.AssetConnection, error) { | |
return loaders(ctx).Asset.FindByWorkspaceProject(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination) | |
} |
server/e2e/seeder.go
Outdated
func addAsset(path string, ctx context.Context, r *repo.Container, gf gateway.File) (*url.URL, error) { | ||
f, err := os.Open(path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer func() { | ||
if cerr := f.Close(); cerr != nil && err == nil { | ||
err = cerr | ||
} | ||
}() | ||
stat, err := f.Stat() | ||
if err != nil { | ||
return nil, err | ||
} | ||
contentType := mime.TypeByExtension(filepath.Ext(path)) | ||
if contentType == "" { | ||
contentType = "application/octet-stream" | ||
} | ||
|
||
u, size, err := gf.UploadAsset(ctx, &file.File{ | ||
Content: f, | ||
Path: path, | ||
Size: int64(stat.Size()), | ||
ContentType: contentType, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
aid := asset.NewID() | ||
a := asset.New(). | ||
ID(aid). | ||
Workspace(wID). | ||
Project(&pID). | ||
URL(u.String()). | ||
CreatedAt(aid.Timestamp()). | ||
Name(path). | ||
Size(size). | ||
ContentType(contentType). | ||
CoreSupport(true). | ||
MustBuild() | ||
if err := r.DatasetSchema.Save(ctx, dss); err != nil { | ||
return u, r.Asset.Save(ctx, a) | ||
} |
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 asset file size and content type.
The function should validate file size and content type before uploading.
func addAsset(path string, ctx context.Context, r *repo.Container, gf gateway.File) (*url.URL, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer func() {
if cerr := f.Close(); cerr != nil && err == nil {
err = cerr
}
}()
stat, err := f.Stat()
if err != nil {
return nil, err
}
+ // Validate file size (e.g., max 10MB)
+ if stat.Size() > 10*1024*1024 {
+ return nil, fmt.Errorf("file size %d exceeds maximum allowed size of 10MB", stat.Size())
+ }
+
contentType := mime.TypeByExtension(filepath.Ext(path))
if contentType == "" {
contentType = "application/octet-stream"
}
+ // Validate content type
+ allowedTypes := map[string]bool{
+ "image/jpeg": true,
+ "image/png": true,
+ "image/gif": true,
+ }
+ if !allowedTypes[contentType] {
+ return nil, fmt.Errorf("unsupported content type: %s", contentType)
+ }
📝 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 addAsset(path string, ctx context.Context, r *repo.Container, gf gateway.File) (*url.URL, error) { | |
f, err := os.Open(path) | |
if err != nil { | |
return nil, err | |
} | |
defer func() { | |
if cerr := f.Close(); cerr != nil && err == nil { | |
err = cerr | |
} | |
}() | |
stat, err := f.Stat() | |
if err != nil { | |
return nil, err | |
} | |
contentType := mime.TypeByExtension(filepath.Ext(path)) | |
if contentType == "" { | |
contentType = "application/octet-stream" | |
} | |
u, size, err := gf.UploadAsset(ctx, &file.File{ | |
Content: f, | |
Path: path, | |
Size: int64(stat.Size()), | |
ContentType: contentType, | |
}) | |
if err != nil { | |
return nil, err | |
} | |
aid := asset.NewID() | |
a := asset.New(). | |
ID(aid). | |
Workspace(wID). | |
Project(&pID). | |
URL(u.String()). | |
CreatedAt(aid.Timestamp()). | |
Name(path). | |
Size(size). | |
ContentType(contentType). | |
CoreSupport(true). | |
MustBuild() | |
if err := r.DatasetSchema.Save(ctx, dss); err != nil { | |
return u, r.Asset.Save(ctx, a) | |
} | |
func addAsset(path string, ctx context.Context, r *repo.Container, gf gateway.File) (*url.URL, error) { | |
f, err := os.Open(path) | |
if err != nil { | |
return nil, err | |
} | |
defer func() { | |
if cerr := f.Close(); cerr != nil && err == nil { | |
err = cerr | |
} | |
}() | |
stat, err := f.Stat() | |
if err != nil { | |
return nil, err | |
} | |
// Validate file size (e.g., max 10MB) | |
if stat.Size() > 10*1024*1024 { | |
return nil, fmt.Errorf("file size %d exceeds maximum allowed size of 10MB", stat.Size()) | |
} | |
contentType := mime.TypeByExtension(filepath.Ext(path)) | |
if contentType == "" { | |
contentType = "application/octet-stream" | |
} | |
// Validate content type | |
allowedTypes := map[string]bool{ | |
"image/jpeg": true, | |
"image/png": true, | |
"image/gif": true, | |
} | |
if !allowedTypes[contentType] { | |
return nil, fmt.Errorf("unsupported content type: %s", contentType) | |
} | |
u, size, err := gf.UploadAsset(ctx, &file.File{ | |
Content: f, | |
Path: path, | |
Size: int64(stat.Size()), | |
ContentType: contentType, | |
}) | |
if err != nil { | |
return nil, err | |
} | |
aid := asset.NewID() | |
a := asset.New(). | |
ID(aid). | |
Workspace(wID). | |
Project(&pID). | |
URL(u.String()). | |
CreatedAt(aid.Timestamp()). | |
Name(path). | |
Size(size). | |
ContentType(contentType). | |
CoreSupport(true). | |
MustBuild() | |
return u, r.Asset.Save(ctx, a) | |
} |
func AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error { | ||
fileName := strings.TrimPrefix(path, "/assets/") | ||
stream, err := file.ReadAsset(ctx, fileName) | ||
if err != nil { | ||
return nil // skip if external URL | ||
} | ||
defer func() { | ||
if cerr := stream.Close(); cerr != nil { | ||
fmt.Printf("Error closing file: %v\n", cerr) | ||
} | ||
}() | ||
zipEntryPath := fmt.Sprintf("assets/%s", fileName) | ||
zipEntry, err := zipWriter.Create(zipEntryPath) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = io.Copy(zipEntry, stream) | ||
if err != nil { | ||
_ = stream.Close() | ||
return err | ||
} | ||
return nil | ||
} |
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
Improve error handling in AddZipAsset.
The function should handle errors more robustly and provide better error messages.
func AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error {
+ if file == nil || zipWriter == nil {
+ return errors.New("file and zipWriter must not be nil")
+ }
+ if path == "" {
+ return errors.New("path must not be empty")
+ }
+
fileName := strings.TrimPrefix(path, "/assets/")
stream, err := file.ReadAsset(ctx, fileName)
if err != nil {
- return nil // skip if external URL
+ // Log skipped external URLs for debugging
+ fmt.Printf("Skipping external URL: %s\n", path)
+ return nil
}
defer func() {
if cerr := stream.Close(); cerr != nil {
- fmt.Printf("Error closing file: %v\n", cerr)
+ fmt.Printf("Warning: Failed to close stream for %s: %v\n", fileName, cerr)
}
}()
zipEntryPath := fmt.Sprintf("assets/%s", fileName)
zipEntry, err := zipWriter.Create(zipEntryPath)
if err != nil {
- return err
+ return fmt.Errorf("failed to create zip entry %s: %w", zipEntryPath, err)
}
_, err = io.Copy(zipEntry, stream)
if err != nil {
_ = stream.Close()
- return err
+ return fmt.Errorf("failed to copy asset %s to zip: %w", fileName, err)
}
return nil
}
📝 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 AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error { | |
fileName := strings.TrimPrefix(path, "/assets/") | |
stream, err := file.ReadAsset(ctx, fileName) | |
if err != nil { | |
return nil // skip if external URL | |
} | |
defer func() { | |
if cerr := stream.Close(); cerr != nil { | |
fmt.Printf("Error closing file: %v\n", cerr) | |
} | |
}() | |
zipEntryPath := fmt.Sprintf("assets/%s", fileName) | |
zipEntry, err := zipWriter.Create(zipEntryPath) | |
if err != nil { | |
return err | |
} | |
_, err = io.Copy(zipEntry, stream) | |
if err != nil { | |
_ = stream.Close() | |
return err | |
} | |
return nil | |
} | |
func AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error { | |
if file == nil || zipWriter == nil { | |
return errors.New("file and zipWriter must not be nil") | |
} | |
if path == "" { | |
return errors.New("path must not be empty") | |
} | |
fileName := strings.TrimPrefix(path, "/assets/") | |
stream, err := file.ReadAsset(ctx, fileName) | |
if err != nil { | |
// Log skipped external URLs for debugging | |
fmt.Printf("Skipping external URL: %s\n", path) | |
return nil | |
} | |
defer func() { | |
if cerr := stream.Close(); cerr != nil { | |
fmt.Printf("Warning: Failed to close stream for %s: %v\n", fileName, cerr) | |
} | |
}() | |
zipEntryPath := fmt.Sprintf("assets/%s", fileName) | |
zipEntry, err := zipWriter.Create(zipEntryPath) | |
if err != nil { | |
return fmt.Errorf("failed to create zip entry %s: %w", zipEntryPath, err) | |
} | |
_, err = io.Copy(zipEntry, stream) | |
if err != nil { | |
return fmt.Errorf("failed to copy asset %s to zip: %w", fileName, err) | |
} | |
return nil | |
} |
edc304a
to
261978e
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 (4)
server/internal/adapter/gql/resolver_mutation_project.go (2)
214-214
:⚠️ Potential issueHandle potential error during workspace ID conversion.
The workspace ID conversion ignores potential errors, which could lead to runtime issues.
- workspace, _ := accountdomain.WorkspaceIDFrom(string(input.TeamID)) + workspace, err := accountdomain.WorkspaceIDFrom(string(input.TeamID)) + if err != nil { + return nil, errors.New("invalid team ID") + }
224-224
:⚠️ Potential issueHandle potential error from unmarshalProject.
The unmarshalProject error is ignored, which could lead to nil dereference if unmarshaling fails.
- projectData, _ := unmarshalProject(tempData) + projectData, err := unmarshalProject(tempData) + if err != nil { + return nil, errors.New("failed to unmarshal project data: " + err.Error()) + }server/e2e/common.go (1)
266-271
:⚠️ Potential issueAdd type assertion error handling in JSONEqRegexpValue.
The type assertion in
JSONEqRegexpValue
should handle the case where the assertion fails.Apply this diff:
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { - if actualData, ok := actual.Raw().(map[string]interface{}); ok { + actualData, ok := actual.Raw().(map[string]interface{}) + if !ok { + t.Errorf("expected map[string]interface{}, got %T", actual.Raw()) + return false + } return JSONEqRegexpInterface(t, actualData, expected) - } - return false }server/e2e/gql_asset_test.go (1)
224-240
:⚠️ Potential issueAdd error handling for updateAsset response.
The function should check for errors in the GraphQL response.
Apply this diff:
func updateAsset(e *httpexpect.Expect, assetId string, projectId *string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "UpdateAsset", Query: `mutation UpdateAsset($assetId: ID!, $projectId: ID) { updateAsset(input: { assetId: $assetId projectId: $projectId }) { assetId projectId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, "projectId": projectId, }, } return Request(e, uID.String(), requestBody) }
🧹 Nitpick comments (5)
server/e2e/gql_storytelling_test.go (2)
15-55
: LGTM! Test coverage for GA fields looks good.The test successfully verifies the new Google Analytics fields. The update operation correctly sets and verifies both
enableGa
andtrackingId
.Consider adding test cases for:
- Updating GA fields independently
- Edge cases with empty tracking ID
- Disabling GA after enabling
232-413
: Add test cases for GA fields in published stories.While the test verifies the default values for GA fields (
enableGa: false
andtrackingId: ""
), it would be beneficial to test publishing with GA enabled and a tracking ID set.Add a test case that:
- Updates the story with GA enabled and a tracking ID
- Publishes the story
- Verifies the published JSON contains the updated GA values
server/internal/adapter/gql/gqlmodel/models_gen.go (1)
1199-1223
: Consider adding validation for Google Analytics tracking ID.While the addition of
enableGa
andtrackingId
fields aligns with the PR objectives, consider:
- Making
trackingId
optional since it's only relevant whenenableGa
is true- Adding validation to ensure the tracking ID follows Google Analytics format (e.g., "UA-XXXXX-Y" or "G-XXXXXXXX")
type Story struct { // ... other fields ... - TrackingID string `json:"trackingId"` + TrackingID *string `json:"trackingId"` }server/internal/usecase/interactor/scene.go (1)
699-701
: Consider refactoring repeated URL asset handling logic.There are multiple instances of similar URL parsing and asset handling code. Consider extracting this into a helper function to improve maintainability and reduce duplication.
+func addURLAssetToZip(ctx context.Context, file gateway.File, zipWriter *zip.Writer, value any) error { + if u, ok := value.(*url.URL); ok { + return AddZipAsset(ctx, file, zipWriter, u.Path) + } + return nil +}Then use it in the existing code:
-if u, ok := field.Value().Value().(*url.URL); ok { - if err := AddZipAsset(ctx, i.file, zipWriter, u.Path); err != nil { - log.Infofc(ctx, "Fail widget addZipAsset :", err.Error()) - } -} +if err := addURLAssetToZip(ctx, i.file, zipWriter, field.Value().Value()); err != nil { + log.Infofc(ctx, "Fail widget addZipAsset :", err.Error()) +}Also applies to: 728-730, 753-755
server/internal/adapter/gql/resolver_mutation_project.go (1)
178-182
: Remove commented-out code.The commented-out code block should be removed as it's no longer needed and may cause confusion.
- // pid2, err := gqlmodel.ToID[id.Project](gqlmodel.ID(prj.ID().String())) - // if err != nil { - // return nil, err - // }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
server/e2e/common.go
(7 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/gql/storytelling.graphql
(3 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(9 hunks)server/internal/adapter/gql/resolver_mutation_project.go
(6 hunks)server/internal/adapter/gql/resolver_mutation_scene.go
(0 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/scene.go
(6 hunks)server/internal/usecase/interactor/storytelling.go
(1 hunks)server/internal/usecase/interactor/storytelling_test.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)
💤 Files with no reviewable changes (1)
- server/internal/adapter/gql/resolver_mutation_scene.go
🚧 Files skipped from review as they are similar to previous changes (11)
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/internal/usecase/interfaces/story.go
- server/internal/usecase/interactor/storytelling.go
- server/pkg/storytelling/story_bulider.go
- server/internal/usecase/interactor/storytelling_test.go
- server/e2e/mock_test.go
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/e2e/gql_project_test.go
- server/gql/storytelling.graphql
- server/pkg/storytelling/story.go
🧰 Additional context used
🧠 Learnings (1)
server/internal/adapter/gql/resolver_mutation_project.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
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 (1)
- GitHub Check: deploy-server / deploy_test
🔇 Additional comments (14)
server/e2e/gql_storytelling_test.go (1)
513-514
: LGTM! GraphQL schema changes are well implemented.The GA fields have been correctly added to both the query and mutation operations. The implementation follows GraphQL best practices with proper typing.
Also applies to: 564-565, 581-582
server/internal/adapter/gql/gqlmodel/models_gen.go (2)
134-145
: LGTM! Asset-Project association is well structured.The addition of the optional
ProjectID
field to Asset types enables proper organization and management of assets within projects. The changes maintain consistency across Asset-related types.Also applies to: 184-189, 1332-1340
829-856
: Clarify precedence of Google Analytics settings.The Google Analytics settings exist at both Project and Story levels. This might cause confusion about which settings take precedence when both are configured.
Consider:
- Documenting the precedence rules
- Adding validation for the tracking ID format
- Making
trackingId
optional in the Project type as welltype Project struct { // ... other fields ... - TrackingID string `json:"trackingId"` + TrackingID *string `json:"trackingId"` }Also applies to: 1397-1418
server/internal/usecase/interactor/scene.go (3)
21-21
: LGTM: Import addition aligns with Story GA support.The addition of the storytelling package import is consistent with the PR's objective of adding Google Analytics support to the Story component.
150-151
: LGTM: Improved property access control.The use of
writableFilter
enhances security by ensuring property writes are properly scoped to the specific scene.
668-671
: LGTM: Safe story data handling.The code safely handles potential nil cases and array bounds for story data, preventing potential runtime panics.
server/internal/adapter/gql/resolver_mutation_project.go (3)
83-84
: LGTM: GA support fields added correctly.The addition of
EnableGa
andTrackingID
fields aligns with the PR objectives and follows the existing parameter pattern.
189-194
: LGTM: Improved URL parsing with proper error handling.The addition of proper URL parsing with error handling enhances the robustness of the asset name extraction process.
229-233
: LGTM: Proper transaction handling.The transaction handling follows best practices with proper error propagation and cleanup in the defer block.
server/e2e/gql_me_test.go (1)
8-8
: LGTM! Server initialization simplified.The change streamlines the server initialization by using the new
Server()
function while maintaining the test's functionality.server/e2e/ping_test.go (1)
9-9
: LGTM! Server initialization simplified.The change streamlines the server initialization by using the new
ServerPingTest()
function while maintaining the test's functionality.server/e2e/common.go (2)
35-43
: LGTM! Good configuration refactoring.Moving common configuration into variables improves code reusability and maintainability.
283-297
: LGTM! Improved ValueDump function.The function now handles different types more robustly with proper type assertions and reflection for unsupported types.
server/e2e/gql_asset_test.go (1)
124-179
: LGTM! Comprehensive project association tests.The test cases thoroughly validate asset-project associations, including:
- Asset creation with project association
- Asset retrieval by project
- Moving assets between projects
- Removing project associations
- Asset deletion
func unmarshalAssets(data []byte) (map[string]string, error) { | ||
var jsonData map[string]interface{} | ||
if err := json.Unmarshal(data, &jsonData); err != nil { | ||
return nil, err | ||
} | ||
|
||
assets, _ := jsonData["assets"].(map[string]interface{}) | ||
assetNames := make(map[string]string) | ||
for k, v := range assets { | ||
strValue, _ := v.(string) | ||
assetNames[k] = strValue | ||
} | ||
|
||
return assetNames, nil | ||
} |
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
Standardize error handling in unmarshal functions.
The unmarshal helper functions have inconsistent error handling. Type assertions should be checked consistently across all functions.
Example for unmarshalAssets:
func unmarshalAssets(data []byte) (map[string]string, error) {
var jsonData map[string]interface{}
if err := json.Unmarshal(data, &jsonData); err != nil {
return nil, err
}
- assets, _ := jsonData["assets"].(map[string]interface{})
+ assets, ok := jsonData["assets"].(map[string]interface{})
+ if !ok {
+ return nil, errors.New("invalid assets data format")
+ }
assetNames := make(map[string]string)
for k, v := range assets {
- strValue, _ := v.(string)
+ strValue, ok := v.(string)
+ if !ok {
+ return nil, fmt.Errorf("invalid asset name format for key %s", k)
+ }
assetNames[k] = strValue
}
return assetNames, nil
}
Apply similar changes to unmarshalPluginsScene.
Also applies to: 375-385
server/e2e/gql_asset_test.go
Outdated
func removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value { | ||
requestBody := GraphQLRequest{ | ||
OperationName: "RemoveAsset", | ||
Query: `mutation RemoveAsset($assetId: ID!) { | ||
removeAsset(input: { assetId: $assetId }) { | ||
assetId | ||
__typename | ||
} | ||
}`, | ||
Variables: map[string]any{ | ||
"assetId": assetId, | ||
}, | ||
} | ||
return Request(e, uID.String(), requestBody) | ||
} |
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 error handling for removeAsset response.
Similar to updateAsset, this function should also check for errors.
Apply this diff:
func removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value {
requestBody := GraphQLRequest{
OperationName: "RemoveAsset",
Query: `mutation RemoveAsset($assetId: ID!) {
removeAsset(input: { assetId: $assetId }) {
assetId
+ errors {
+ message
+ path
+ }
__typename
}
}`,
Variables: map[string]any{
"assetId": assetId,
},
}
return Request(e, uID.String(), requestBody)
}
📝 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 removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value { | |
requestBody := GraphQLRequest{ | |
OperationName: "RemoveAsset", | |
Query: `mutation RemoveAsset($assetId: ID!) { | |
removeAsset(input: { assetId: $assetId }) { | |
assetId | |
__typename | |
} | |
}`, | |
Variables: map[string]any{ | |
"assetId": assetId, | |
}, | |
} | |
return Request(e, uID.String(), requestBody) | |
} | |
func removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value { | |
requestBody := GraphQLRequest{ | |
OperationName: "RemoveAsset", | |
Query: `mutation RemoveAsset($assetId: ID!) { | |
removeAsset(input: { assetId: $assetId }) { | |
assetId | |
errors { | |
message | |
path | |
} | |
__typename | |
} | |
}`, | |
Variables: map[string]any{ | |
"assetId": assetId, | |
}, | |
} | |
return Request(e, uID.String(), requestBody) | |
} |
10e3df1
to
e394704
Compare
e394704
to
6f6caec
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 (3)
server/e2e/gql_project_export_import_test.go (1)
29-29
: 🛠️ Refactor suggestionAdd error handling for HTTP response in
exporProject
.The function should check the HTTP response status and handle potential errors before writing to file.
downloadResponse := e.GET(fmt.Sprintf("http://localhost:8080%s", downloadPath)). Expect(). Status(http.StatusOK). Body().Raw() +if len(downloadResponse) == 0 { + t.Fatal("Empty response from export endpoint") +} fileName := "project_data.zip"server/e2e/seeder.go (2)
203-205
:⚠️ Potential issueFix critical error in conditional logic.
The error check in this line is incorrect.
err != err
will always evaluate tofalse
, making the error handling code unreachable.Apply this diff to fix the issue:
- if err = r.Scene.Save(ctx, s); err != err { + if err = r.Scene.Save(ctx, s); err != nil { return err }
206-208
:⚠️ Potential issueFix all remaining incorrect error check conditions.
Similarly, all of these error checks use
err != err
which is logically incorrect and will never evaluate to true. This causes the error handling code to be unreachable.Fix all instances by replacing with
err != nil
:- if err != err { + if err != nil { return err }Also applies to: 209-211, 212-214, 215-217, 218-220, 221-223, 266-268, 269-271, 297-299, 312-314, 350-352
🧹 Nitpick comments (12)
server/internal/app/app.go (2)
72-78
: Improved auth configuration with conditional middlewareThe changes to the authentication logic are well-structured, adding support for disabling authentication server while maintaining the existing JWT provider functionality. This approach allows for more flexibility in development and testing environments.
However, I notice that the auth config is logged twice - once here and again on line 138. Consider consolidating these log statements to avoid duplication.
- authConfig := cfg.Config.JWTProviders() - log.Infof("auth: config: %#v", authConfig) + authConfig := cfg.Config.JWTProviders()And modify line 138 to include both pieces of information:
- log.Infofc(ctx, "auth: config: %#v", cfg.Config.AuthSrv) + log.Infofc(ctx, "auth: server config: %#v, jwt providers: %#v", cfg.Config.AuthSrv, authConfig)
164-168
: Simple but effective dummy auth middleware implementationThis implementation correctly provides a pass-through middleware when authentication is disabled. The function signature matches what's expected by the caller, and the implementation is minimal yet functional.
Since this is a security-related feature, consider adding a comment explaining that this middleware should only be used in development/testing environments where security isn't a concern.
func AuthMiddlewareDummy() (func(http.Handler) http.Handler, error) { + // WARNING: This middleware bypasses authentication entirely and should only be used + // in controlled environments like testing or local development where security isn't a concern. return func(next http.Handler) http.Handler { return next }, nil }server/internal/adapter/gql/resolver_mutation_project.go (1)
177-182
: Remove commented-out code.This commented-out code block is unnecessary and should be removed to maintain code cleanliness.
-// pid2, err := gqlmodel.ToID[id.Project](gqlmodel.ID(prj.ID().String())) -// if err != nil { -// return nil, err -// }server/internal/usecase/interactor/project.go (1)
627-649
: Improve error handling in AddZipAsset.The function should handle errors more robustly and provide better error messages.
func AddZipAsset(ctx context.Context, file gateway.File, zipWriter *zip.Writer, path string) error { + if file == nil || zipWriter == nil { + return errors.New("file or zipWriter is nil") + } + if path == "" { + return errors.New("asset path cannot be empty") + } + fileName := strings.TrimPrefix(path, "/assets/") stream, err := file.ReadAsset(ctx, fileName) if err != nil { - return nil // skip if external URL + // Skip external URLs but log for debugging + return nil } defer func() { if cerr := stream.Close(); cerr != nil { - fmt.Printf("Error closing file: %v\n", cerr) + fmt.Printf("Warning: Failed to close stream for %s: %v\n", fileName, cerr) } }() zipEntryPath := fmt.Sprintf("assets/%s", fileName) zipEntry, err := zipWriter.Create(zipEntryPath) if err != nil { - return err + return fmt.Errorf("failed to create zip entry %s: %w", zipEntryPath, err) } _, err = io.Copy(zipEntry, stream) if err != nil { _ = stream.Close() - return err + return fmt.Errorf("failed to copy asset %s to zip: %w", fileName, err) } return nil }server/e2e/gql_project_export_import_test.go (4)
66-70
: Consider adding error handling for JSON unmarshaling failures.The function assumes the value can always be marshaled into JSON. If the value is nil or otherwise invalid, this could panic.
func convertLine(t *testing.T, key string, v *httpexpect.Value) []string { + if v == nil || v.Raw() == nil { + t.Fatalf("value for key %s is nil", key) + return []string{} + } v2, err := json.MarshalIndent(v.Object().Value(key).Raw(), "", " ") assert.Nil(t, err) return strings.Split(strings.ReplaceAll(string(v2), "\r\n", "\n"), "\n") }
72-102
: Consider using a structured comparison library for deeper JSON comparison.The current line-by-line comparison approach with various exceptions is functional but can be brittle. Consider using a library designed for JSON comparison with customizable rules.
The current implementation works well for this specific test case but might be less maintainable as the data structures change. A structured comparison library would provide more flexibility and better error messages.
113-120
: Broaden ID validation to support multiple ID types.The current implementation only validates against
id.Widget
type. Consider expanding this to support other ID types used in the application.func isID(text string) bool { v := strings.TrimSpace(text) v = strings.Trim(v, `"`) - if _, err := idx.From[id.Widget](v); err != nil { + // Try parsing as different ID types + if _, err := idx.From[id.Widget](v); err == nil { + return true + } + if _, err := idx.From[id.Scene](v); err == nil { + return true + } + if _, err := idx.From[id.Story](v); err == nil { return true } - return true + return false }
122-141
: Ensure zip file creation doesn't conflict with existing files.The current implementation creates a file with a hardcoded name, which could cause issues in parallel test execution.
- fileName := "project_data.zip" + fileName := fmt.Sprintf("project_data_%s.zip", p) // Use project ID to make filename unique err := os.WriteFile(fileName, []byte(downloadResponse), os.ModePerm) assert.Nil(t, err) return fileNameserver/e2e/gql_storytelling_test.go (1)
232-413
: Consider using a more maintainable approach for complex JSON assertions.The current test uses a large string with regular expressions for dynamic values, which could become brittle if the JSON structure changes. Consider using structured assertions or a JSON schema validation approach.
This test is comprehensive but might be difficult to maintain. Consider breaking it down into smaller, more focused assertions or using a JSON schema validation library.
server/internal/adapter/gql/generated.go (2)
8162-8162
: Inserted comment marker# Publishment
.
No functional change.
56783-56786
: Root resolver referencesupdateAsset
in mutations.
Ensure consistent testing coverage.server/e2e/seeder.go (1)
507-522
: Consider using a standard UUID library.While your manual UUID generation follows the UUID v4 specifications, consider using a standard library like
github.com/google/uuid
for better maintainability and RFC 4122 compliance.- func generateUUID() string { - b := make([]byte, 16) - _, err := rand.Read(b) - if err != nil { - panic(err) - } - b[6] = (b[6] & 0x0F) | 0x40 - b[8] = (b[8] & 0x3F) | 0x80 - return fmt.Sprintf("%s-%s-%s-%s-%s", - hex.EncodeToString(b[0:4]), - hex.EncodeToString(b[4:6]), - hex.EncodeToString(b[6:8]), - hex.EncodeToString(b[8:10]), - hex.EncodeToString(b[10:16]), - ) - } + import "github.com/google/uuid" + + func generateUUID() string { + return uuid.New().String() + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
server/e2e/test.zip
is excluded by!**/*.zip
📒 Files selected for processing (58)
server/e2e/common.go
(7 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 hunks)server/gql/storytelling.graphql
(3 hunks)server/gql/workspace.graphql
(2 hunks)server/gqlgen.yml
(0 hunks)server/internal/adapter/gql/generated.go
(61 hunks)server/internal/adapter/gql/gqlmodel/convert_asset.go
(1 hunks)server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(5 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 hunks)server/internal/adapter/gql/resolver_query.go
(1 hunks)server/internal/adapter/gql/resolver_team.go
(1 hunks)server/internal/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/nlslayer.go
(3 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/scene.go
(5 hunks)server/internal/usecase/interactor/storytelling.go
(2 hunks)server/internal/usecase/interactor/storytelling_test.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 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/nlslayer/feature.go
(0 hunks)server/pkg/nlslayer/feature_test.go
(1 hunks)server/pkg/scene/builder/story.go
(1 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)
💤 Files with no reviewable changes (5)
- server/internal/usecase/interactor/project_test.go
- server/gqlgen.yml
- server/e2e/gql_project_export_test.go
- server/pkg/nlslayer/feature.go
- server/internal/infrastructure/mongo/mongodoc/scene.go
🚧 Files skipped from review as they are similar to previous changes (39)
- server/pkg/asset/builder.go
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/internal/usecase/interfaces/story.go
- server/e2e/mock_test.go
- server/internal/adapter/gql/resolver_query.go
- server/go.mod
- server/e2e/gql_user_test.go
- server/pkg/storytelling/story_bulider.go
- server/internal/usecase/interactor/storytelling_test.go
- server/pkg/nlslayer/feature_test.go
- server/internal/adapter/gql/gqlmodel/convert_asset.go
- server/e2e/gql_me_test.go
- server/gql/storytelling.graphql
- server/internal/infrastructure/fs/file.go
- server/pkg/scene/builder/story.go
- server/pkg/asset/id.go
- server/internal/usecase/repo/asset.go
- server/internal/infrastructure/fs/file_test.go
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/internal/adapter/gql/loader_asset.go
- server/e2e/gql_validate_geojson_test.go
- server/internal/app/auth_client.go
- server/pkg/asset/asset.go
- server/e2e/gql_scene_test.go
- server/e2e/gql_project_test.go
- server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go
- server/internal/usecase/interfaces/project.go
- server/internal/adapter/gql/resolver_team.go
- server/internal/infrastructure/mongo/mongodoc/asset.go
- server/gql/asset.graphql
- server/internal/usecase/interactor/storytelling.go
- server/internal/usecase/interactor/nlslayer.go
- server/pkg/storytelling/story.go
- server/internal/infrastructure/memory/asset.go
- server/gql/workspace.graphql
- server/internal/usecase/interfaces/asset.go
- server/internal/adapter/gql/gqlmodel/models_gen.go
- server/e2e/common.go
🧰 Additional context used
🧠 Learnings (2)
server/internal/adapter/gql/resolver_mutation_project.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
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`.
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
🔇 Additional comments (104)
server/e2e/ping_test.go (1)
9-9
:✅ Verification successful
Server initialization refactored
The server initialization has been simplified by using the
ServerPingTest
function instead of the previous explicit configuration. This improves code readability and maintainability. Make sure thatServerPingTest
correctly sets up the CORS configuration to allow "https://example.com" as expected by the test.
🏁 Script executed:
#!/bin/bash # Check the implementation of ServerPingTest to verify CORS configuration grep -r "ServerPingTest" server/e2e/ --include="*.go" -A 10Length of output: 1164
Server Initialization Refactored – CORS Configuration Confirmed
The refactoring to use
ServerPingTest
is verified to be working as expected. The test inserver/e2e/ping_test.go
correctly sets and validates the CORS configuration by checking that requests with the "Origin" header of "https://example.com" receive the appropriate "Access-Control-Allow-Origin" header. The implementation inserver/e2e/common.go
shows that the server is properly initialized with the expected origins configuration. No further changes are necessary.server/internal/app/app.go (1)
72-78
:❓ Verification inconclusive
Verify security implications of disabled auth server
While the implementation is technically correct, disabling the auth server could have significant security implications. Ensure that this feature is properly documented and that there are appropriate safeguards to prevent it from being enabled in production environments.
🏁 Script executed:
#!/bin/bash # Check if there are any tests that verify the auth server can't be disabled in production rg -A 3 -B 3 "AuthSrv.Disabled" --glob "*.go" | grep -i "prod\|environment\|env" # Check for any configuration validation that prevents disabling auth in production rg -A 5 "ValidateConfig\|validateConfig\|ConfigValidation" --glob "*.go" | grep -A 5 -i "auth"Length of output: 175
Security Implications of Disabled Authentication in Production
After executing our initial search scripts, we found no evidence of tests or configuration validations that ensure the auth server cannot be accidentally disabled in production. This raises potential security concerns regarding the use of the
AuthSrv.Disabled
flag.
- Ensure Production Safeguards: Manually verify that the production environment cannot run with authentication disabled. This might involve:
- Adding explicit configuration validation that disallows disabling auth in production.
- Documenting the security impact clearly in the configuration and deployment guides.
- Integrating tests to enforce that the auth server remains enabled in production settings.
Please review the configuration validations and test suites to confirm that proper safeguards are in place.
server/internal/adapter/gql/gqlmodel/convert_project.go (3)
108-113
: NewProjectExport
type structure looks good.The structure includes essential project fields (visualizer, name, description, and imageURL), with proper JSON tag naming conventions. The use of
*url.URL
forImageURL
and marking it asomitempty
is appropriate for handling optional URL values.
115-125
: Clean implementation of the conversion function.The function properly handles nil input checking and field mapping from the project entity to the export structure. This function follows the established pattern for entity conversions in the codebase.
127-137
: Consider improving error handling and JSON conversion.The implementation has the same issues as noted in previous reviews.
Apply this diff to improve the implementation:
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { var p ProjectExport - bytes, err := json.MarshalIndent(data, "", " ") + bytes, err := json.Marshal(data) if err != nil { - return nil + return nil // Consider logging the error or returning it } if err := json.Unmarshal(bytes, &p); err != nil { - return nil + return nil // Consider logging the error or returning it } return &p }server/internal/infrastructure/mongo/asset.go (2)
67-81
: Good implementation of project filtering in asset retrieval.The function has been renamed from
FindByWorkspace
toFindByWorkspaceProject
to better reflect its expanded functionality. The implementation correctly handles both cases - filtering by project ID when provided or falling back to team-based filtering. The code structure is clean and follows the appropriate patterns for MongoDB queries.
96-100
: Handle type assertion failure case.The type assertion could silently fail if unsuccessful.
Apply this diff to handle the failure case:
- if andFilter, ok := mongox.And(filter, "url", bson.M{ - "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, - }).(bson.M); ok { - filter = andFilter - } + if andResult := mongox.And(filter, "url", bson.M{ + "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, + }); andResult != nil { + if andFilter, ok := andResult.(bson.M); ok { + filter = andFilter + } + }server/internal/adapter/gql/resolver_mutation_asset.go (2)
19-26
: Good implementation of optional project ID handling.The code correctly handles the optional project ID parameter, performs proper type conversion, and provides appropriate error handling. The structure follows established patterns for GraphQL resolvers in the codebase.
41-62
: Well-structured implementation of the UpdateAsset mutation.The new mutation handler is well-structured and follows the established patterns for mutation resolvers. It properly handles parameter validation, ID conversion, and includes appropriate error handling. The response payload properly returns both the asset ID and optional project ID.
server/internal/usecase/interactor/scene.go (4)
668-671
: Fixed potential nil pointer dereference.The code now safely handles the case where the story list could be nil or empty, preventing a potential nil pointer dereference that would occur in the previous implementation. This is a good defensive programming practice.
699-702
: Simplified asset handling with direct AddZipAsset calls.The code now directly uses
AddZipAsset
instead of a helper function, making the code more straightforward. The error handling is appropriate, logging failures without interrupting the overall export process.
728-732
: Type assertion and error handling look good.The code properly checks if the field value is a URL type before attempting to use it and handles any errors that might occur during asset addition by logging them without interrupting the export process.
753-756
: Consistent implementation of asset handling.This section maintains consistency with the earlier patterns for asset handling, properly checking the type and handling any errors during asset addition.
server/internal/adapter/gql/resolver_mutation_project.go (8)
130-130
: Improved zip filename using direct project ID.The code now uses the project ID directly for naming the zip file, which provides better traceability and debugging.
189-195
: Improved asset filename handling with URL parsing.The new URL parsing approach is more robust for extracting filenames from URLs.
197-200
: Structured approach for building export data.Using conversion functions to format project, plugins, and schemas data improves code clarity and maintainability.
214-214
: Handle potential error during workspace ID conversion.
workspace, _ := accountdomain.WorkspaceIDFrom(string(input.TeamID))
discards the error. IfTeamID
is invalid, subsequent workspace-based operations may fail unexpectedly.- workspace, _ := accountdomain.WorkspaceIDFrom(string(input.TeamID)) + workspace, err := accountdomain.WorkspaceIDFrom(string(input.TeamID)) + if err != nil { + return nil, errors.New("invalid team ID") + }
224-224
: Check potential JSON unmarshal errors.
projectData, _ := unmarshalProject(tempData)
discards errors. Consider handling them to avoid nil references if unmarshalling fails.
229-234
: Ensure proper transaction handling.The transaction handling pattern is well implemented with proper deferred cleanup.
257-265
: Good approach for updating project image.The code correctly updates the project's image URL when a matching asset is found in the import data.
359-373
: Standardize error handling in unmarshal functions.The unmarshal helper functions have inconsistent error handling. Type assertions should be checked consistently across all functions.
Example for unmarshalAssets:
func unmarshalAssets(data []byte) (map[string]string, error) { var jsonData map[string]interface{} if err := json.Unmarshal(data, &jsonData); err != nil { return nil, err } - assets, _ := jsonData["assets"].(map[string]interface{}) + assets, ok := jsonData["assets"].(map[string]interface{}) + if !ok { + return nil, errors.New("invalid assets data format") + } assetNames := make(map[string]string) for k, v := range assets { - strValue, _ := v.(string) + strValue, ok := v.(string) + if !ok { + return nil, fmt.Errorf("invalid asset name format for key %s", k) + } assetNames[k] = strValue } return assetNames, nil }server/e2e/gql_asset_test.go (7)
15-17
: Improved test function naming.Renaming from
TestGetAssets
toTestCoreSupportGetAssets
better indicates the test's specific purpose.
22-62
: Enhanced test assertions using JSONEqRegexpValue.The test now uses a more structured approach with
JSONEqRegexpValue
for validating asset data, which provides better readability and error messages.
106-113
: Updated repository call to support project filtering.The test correctly uses the new
FindByWorkspaceProject
method with anil
project ID parameter.
124-179
: Comprehensive test for asset-project associations.This new test function thoroughly validates the full lifecycle of asset-project associations:
- Creating projects and associating assets
- Querying assets by project
- Moving assets between projects
- Removing project associations
- Asset deletion
Great test coverage!
224-240
: Add error handling for updateAsset response.The function should check for errors in the GraphQL response.
Apply this diff to add error handling:
func updateAsset(e *httpexpect.Expect, assetId string, projectId *string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "UpdateAsset", Query: `mutation UpdateAsset($assetId: ID!, $projectId: ID) { updateAsset(input: { assetId: $assetId projectId: $projectId }) { assetId projectId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, "projectId": projectId, }, } return Request(e, uID.String(), requestBody) }
242-256
: Add error handling for removeAsset response.Similar to updateAsset, this function should also check for errors.
Apply this diff:
func removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "RemoveAsset", Query: `mutation RemoveAsset($assetId: ID!) { removeAsset(input: { assetId: $assetId }) { assetId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, }, } return Request(e, uID.String(), requestBody) }
287-347
: Updated query with project filtering support.The
getAssets
function has been enhanced to support project-based filtering, which aligns with the new API capabilities.server/internal/usecase/interactor/asset.go (5)
39-51
: Renamed function to better reflect its capabilities.The function has been appropriately renamed from
FindByWorkspace
toFindByWorkspaceProject
to reflect its extended functionality for filtering assets by project.
91-91
: Added project association during asset creation.Assets can now be associated with projects during creation, enhancing asset organization capabilities.
108-127
: Validate workspace-to-project consistency.When updating an existing asset's project, the code does not explicitly confirm the new project is in the same workspace or that the operator can write to it. This might allow transferring an asset to a project in a workspace where the user lacks permission.
+ // Validate that the new project belongs to the same workspace and the operator can write to it + if pid != nil { + proj, err := i.repos.Project.FindByID(ctx, *pid) + if err != nil { + return aid, pid, err + } + if proj.Workspace() != asset.Workspace() { + return aid, pid, errors.New("project must be in the same workspace as the asset") + } + if !operator.IsWritableWorkspace(proj.Workspace()) { + return aid, pid, interfaces.ErrOperationDenied + } + }
154-154
: Improved function naming and signature.Renaming from
UploadAssetFile
toImportAssetFiles
better reflects its purpose, and adding the project parameter enhances asset organization.
181-182
: Added project association during asset import.Assets can now be associated with projects during import, creating a consistent approach across creation methods.
server/internal/usecase/interactor/project.go (4)
513-513
: Renamed function for clarity.Renaming from
ExportProject
toExportProjectData
better reflects the function's purpose.
526-526
: Refactored asset handling for better encapsulation.Asset handling has been moved to a dedicated helper function, improving code organization and reusability.
566-566
: Updated function signature for better type safety.The function now takes a strongly-typed workspace parameter instead of a string, improving type safety.
579-588
: Improved project creation from imported data.The code now uses the existing
Create
method to create a project from imported data, ensuring consistency in project creation across the codebase.server/e2e/gql_project_export_import_test.go (2)
59-62
: Good cleanup of test resources.The deferred removal of the exported file ensures that tests clean up after themselves, which is a good practice to prevent test file accumulation.
143-173
: Good use of defer for file closing.The function properly ensures the file is closed even if an error occurs during file operations.
server/e2e/gql_storytelling_test.go (5)
51-52
: Good test coverage for new Google Analytics fields.The test correctly verifies the new
enableGa
andtrackingId
fields added to the Story type.
513-514
: Verification of Google Analytics fields in query.The fields
enableGa
andtrackingId
have been correctly added to the GraphQL query, ensuring they can be fetched and tested.
564-565
: Good implementation of GA parameters in the mutation.The GraphQL mutation for updating a story has been properly extended to include the new Google Analytics parameters.
581-582
: Test values for Google Analytics parameters.The test uses clear, purposeful values for
enableGa
andtrackingId
, making it obvious that these are test values.
869-904
: Simplified assertion in removeBlock function.The function now uses a cleaner assertion to verify that the block has been removed.
server/internal/adapter/gql/generated.go (59)
117-117
: AddedProjectID
complexity function.
No issues found.
498-498
: IntroducedUpdateAsset
complexity function.
Looks consistent with the existing pattern.
803-803
: AddedprojectID
parameter toAssets
query complexity.
Ensure proper authorization checks when filtering by project.
922-922
: AddedenableGa
complexity function forStory
.
No issues found.
938-938
: AddedtrackingId
complexity function forStory
.
No issues found.
987-987
: NewAssets
complexity inTeam
.
Implementation looks fine.
1025-1028
: DefinedUpdateAssetPayload
in complexity map.
No concerns here.
1214-1214
: DeclaredUpdateAsset
inMutationResolver
.
Please verify that only authorized users can update assets.
1351-1351
: Added optionalprojectID
inAssets
query for filtering.
Confirm access control logic.
1405-1405
: AddedprojectID
toTeamResolver.Assets
.
No issues found.
1515-1520
: IncludedAsset.projectId
in complexity selection.
Looks consistent with how other asset fields are implemented.
3544-3555
: IntegratedMutation.updateAsset
complexity.
This aligns well with the new payload.
5251-5251
: ExtendedQuery.Assets
to handle assignment ofprojectID
.
Implementation appears correct.
5803-5809
: IntroducedenableGa
field forStory
complexity.
Matches the PR objective.
5915-5921
: IntroducedtrackingId
field forStory
complexity.
Implementation looks straightforward.
6142-6142
: Added optionalprojectId
inTeam.Assets
query complexity.
No issues found.
6310-6323
: AddedUpdateAssetPayload
complexity fields (assetId
,projectId
).
No issues.
6702-6702
: RegisteredUpdateAssetInput
for unmarshalling.
All good here.
6954-6954
:projectId
added toAsset
schema.
Implementation seems correct.
6978-6981
:UpdateAssetInput
introduced.
No issues noted.
6998-7001
:UpdateAssetPayload
type declared.
Aligns with the new mutation.
7024-7024
:projectId
inassets
query definition.
No issues found.
7033-7033
:updateAsset
mutation inMutation
definition.
Implementation consistent with payload changes.
8110-8111
: AddedenableGa
,trackingId
(non-null) toStory
type.
Make sure the front-end always provides these fields.
8172-8175
: OptionalenableGa
/trackingId
inUpdateStoryInput
.
Ensures partial updates.
8627-8640
: Addedassets(projectId: ID...)
&projects(...)
toTeam
.
This provides flexible queries.
8740-8742
: Changed lines inremoveMemberFromTeam
.
Unrelated to GA changes; no issues.
10237-10263
: Arguments extraction forUpdateAsset
mutation.
Implementation matches the new input type.
11113-11132
: HandlingprojectId
param insideQuery.assets
argument parsing.
Implementation looks correct.
11153-11169
:field_Query_assets_argsProjectID
introduced.
No concerns.
11679-11704
: HandlingprojectId
forTeam.assets
argument parsing.
All good.
12604-12642
:_Asset_projectId
field resolver.
Implementation consistent with optional project ID logic.
13019-13020
: Field context references for_Asset_projectId
.
No issues found.
13224-13225
: Additional references to_Asset_projectId
in field context.
No issues.
13598-13599
: Field context references to_Asset_projectId
(again).
All consistent.
13954-13957
:enableGa
,trackingId
inStory
field context.
No concerns.
14328-14331
:enableGa
,trackingId
field context forStory
repeated.
Implementation looks fine.
22555-22558
: More occurrences of_Story_enableGa
&_Story_trackingId
.
No issues found.
22807-22810
: Recurring_Story_enableGa
&_Story_trackingId
references.
All good.
23031-23034
: Story fieldsenableGa
,trackingId
repeated.
No issues.
23186-23244
:_Mutation_updateAsset
field & resolver method.
Make sure to validate user permissions in actual resolver logic.
37265-37265
: Invokedec.resolvers.Query().Assets
withprojectId
.
Check authorization in the query resolver.
38863-38866
: Referencing_Story_enableGa
&_Story_trackingId
.
No new concerns.
39801-39804
: Repetition ofenableGa
,trackingId
references inStory
.
Looks fine.
41920-42006
:_Story_enableGa
&_Story_trackingId
fields with non-null checks.
Implementation aligns with the schema definition.
43209-43212
: Additional references toenableGa
&trackingId
.
No issues found.
43301-43304
: Continuation ofenableGa
&trackingId
references.
No problems.
43867-43867
:ec.resolvers.Team().Assets
call with optionalprojectId
.
Please confirm permission checks in the resolver.
44693-44777
:_UpdateAssetPayload_assetId
and_UpdateAssetPayload_projectId
.
Payload structure is correct.
49478-49478
: Reordered fields forCreateAssetInput
to includeprojectId
.
No concerns.
49492-49492
: AssignedprojectId
inCreateAssetInput
switching logic.
Implementation is consistent.
51273-51305
:unmarshalInputUpdateAssetInput
function introduced.
No issues identified.
51899-51899
: InsertenableGa
,trackingId
inUpdateStoryInput
field order.
Implementation is correct.
52011-52024
: Handling optionalenableGa
/trackingId
during story updates.
No issues.
52963-52963
: Rendering_Asset_projectId
inAsset
field selection.
Looks good.
61476-61485
:_Story_enableGa
&_Story_trackingId
in field rendering.
Implementation is correct.
62386-62425
:_UpdateAssetPayload
marshalling logic.
No concerns.
66293-66296
:unmarshalNUpdateAssetInput
non-null logic.
No issues found.
67705-67709
:marshalOUpdateAssetPayload
optional logic.
Implementation is consistent with the schema.server/e2e/seeder.go (1)
121-163
: Add validation for asset file size and content type.The function should validate file size and content type before uploading to prevent potential security issues.
func addAsset(path string, ctx context.Context, r *repo.Container, gf gateway.File) (*url.URL, error) { f, err := os.Open(path) if err != nil { return nil, err } defer func() { if cerr := f.Close(); cerr != nil && err == nil { err = cerr } }() stat, err := f.Stat() if err != nil { return nil, err } + // Validate file size (e.g., max 10MB) + if stat.Size() > 10*1024*1024 { + return nil, fmt.Errorf("file size %d exceeds maximum allowed size of 10MB", stat.Size()) + } + contentType := mime.TypeByExtension(filepath.Ext(path)) if contentType == "" { contentType = "application/octet-stream" } + // Validate content type + allowedTypes := map[string]bool{ + "image/jpeg": true, + "image/png": true, + "image/gif": true, + } + if !allowedTypes[contentType] { + return nil, fmt.Errorf("unsupported content type: %s", contentType) + }
server/e2e/seeder.go
Outdated
return err | ||
} |
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.
Fix potential error handling bug.
The function ends with return err
after several operations. If errors occurred earlier but were later successful, this would return the last error value (which could be nil) even though earlier errors occurred.
- return err
+ return nil
📝 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.
return err | |
} | |
return nil | |
} |
server/e2e/seeder.go
Outdated
func addStory(ctx context.Context, r *repo.Container) error { | ||
schema := builtin.GetPropertySchema(builtin.PropertySchemaIDStory) | ||
prop, err := property.New().NewID().Schema(schema.ID()).Scene(sID).Build() | ||
if err != nil { | ||
return err | ||
} | ||
pages := []*storytelling.Page{} | ||
page, err := _createPage(ctx, r) | ||
if err != nil { | ||
return err | ||
} | ||
pages = append(pages, page) | ||
story, err := storytelling.NewStory(). | ||
ID(storyID). | ||
Title("test page"). | ||
Pages(storytelling.NewPageList(pages)). | ||
Scene(sID). | ||
Property(prop.ID()). | ||
Build() | ||
if err != nil { | ||
return err | ||
} | ||
if err = r.Property.Save(ctx, prop); err != err { | ||
return err | ||
} | ||
if err := r.Storytelling.Save(ctx, *story); err != nil { | ||
return err | ||
} | ||
return err | ||
} |
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.
❓ Verification inconclusive
Add Google Analytics parameters to Story component.
According to the PR objectives, you need to add enableGa
and trackingId
parameters to the Story component. These aren't currently implemented in the addStory
function.
story, err := storytelling.NewStory().
ID(storyID).
Title("test page").
Pages(storytelling.NewPageList(pages)).
Scene(sID).
Property(prop.ID()).
+ EnableGa(true).
+ TrackingId("UA-12345678-1").
Build()
🏁 Script executed:
#!/bin/bash
# Check if the Story struct has the enableGa and trackingId fields
# Search for Story struct definition
rg -n "type Story struct" --type go
# Check if the fields exist in the Story struct
rg -A 20 "type Story struct" --type go | grep -E "EnableGa|TrackingId"
# Search for NewStory builder function to see if it supports these fields
rg -n "func NewStory" --type go
Length of output: 1172
Action Required: Update the Google Analytics integration in the Story creation flow
It has been verified that the current implementation of the Story component does not support the Google Analytics parameters—namely, EnableGa
and TrackingId
. The Story struct (in server/pkg/storytelling/story.go
) and its corresponding builder (in server/pkg/storytelling/story_bulider.go
) do not appear to expose or set these fields, which means that simply adding the builder method calls in the addStory
function won’t have the intended effect.
Please address the following:
- In
server/e2e/seeder.go
(lines 275–304): Update the builder chain in theaddStory
function to include the new parameters:story, err := storytelling.NewStory(). ID(storyID). Title("test page"). Pages(storytelling.NewPageList(pages)). Scene(sID). Property(prop.ID()). + EnableGa(true). + TrackingId("UA-12345678-1"). Build()
- In the Story implementation (likely in
server/pkg/storytelling/story.go
andserver/pkg/storytelling/story_bulider.go
):
Ensure that both the Story struct and its builder are updated to define and correctly process theEnableGa
andTrackingId
fields. This may involve:- Adding the new fields to the Story struct.
- Implementing the corresponding builder methods that set these fields in the final Story object.
Once these changes are made, please verify that the Story component correctly handles the analytics parameters during story creation.
[tag: fix_required]
Committable suggestion skipped: line range outside the PR's diff.
3264116
to
07ea4c4
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: 1
🔭 Outside diff range comments (1)
server/internal/usecase/interactor/asset.go (1)
39-49
: 🛠️ Refactor suggestionValidate project-workspace consistency for read operations
Verify that the specified project, if present, belongs to the given workspace and that the user can read it.
♻️ Duplicate comments (2)
server/e2e/gql_asset_test.go (2)
224-240
: 🛠️ Refactor suggestionAdd error handling for updateAsset response.
The function should check for errors in the GraphQL response.
Apply this diff to add error handling:
func updateAsset(e *httpexpect.Expect, assetId string, projectId *string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "UpdateAsset", Query: `mutation UpdateAsset($assetId: ID!, $projectId: ID) { updateAsset(input: { assetId: $assetId projectId: $projectId }) { assetId projectId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, "projectId": projectId, }, } return Request(e, uID.String(), requestBody) }
242-256
: 🛠️ Refactor suggestionAdd error handling for removeAsset response.
Similar to updateAsset, this function should also check for errors.
Apply this diff:
func removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "RemoveAsset", Query: `mutation RemoveAsset($assetId: ID!) { removeAsset(input: { assetId: $assetId }) { assetId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, }, } return Request(e, uID.String(), requestBody) }
🧹 Nitpick comments (13)
server/pkg/nlslayer/feature_test.go (2)
13-14
: Consider renaming the test function to match the new implementation.The test function is still named
TestNewFeatureWithNewId
but it's now testing theNewFeature
function with a generated ID. Renaming it to something likeTestNewFeatureWithGeneratedId
would better reflect what's being tested and avoid confusion for future developers.-func TestNewFeatureWithNewId(t *testing.T) { +func TestNewFeatureWithGeneratedId(t *testing.T) {
9-24
: Consider consolidating duplicate test code.There's significant duplication between
TestNewFeatureWithNewId
andTestNewFeature
. Both tests are very similar with the only real difference being how the ID is created. Consider creating a helper function or using table-driven tests to reduce duplication and improve maintainability.func TestNewFeature(t *testing.T) { testCases := []struct { name string idProvider func() FeatureID }{ { name: "with generated ID", idProvider: func() FeatureID { return NewFeatureID() }, }, { name: "with explicit ID", idProvider: func() FeatureID { return NewFeatureID() }, // Same for test, but makes intent clear }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { id := tc.idProvider() featureType := "Feature" point := NewPoint("Point", []float64{1, 2}) properties := map[string]any{"key1": "value1"} f, err := NewFeature( id, featureType, point, ) f.UpdateProperties(&properties) assert.NoError(t, err) assert.Equal(t, id, f.ID()) assert.Equal(t, featureType, f.FeatureType()) assert.Equal(t, point, f.Geometry()) assert.Equal(t, properties, *f.Properties()) }) } }web/src/services/i18n/translations/en.yml (1)
130-130
: Ensure Default Text for "More" Key
The "More:" key is newly added with an empty default value. Please verify if an empty string is intended or if a placeholder/default text should be provided for clarity in the UI.web/src/beta/features/Navbar/LeftSection/index.tsx (1)
75-80
: Consistent tooltip implementation with minor indentation issue.The tooltipText implementation is good, but there's an indentation inconsistency on line 79 (two extra spaces).
- tooltipText={t("Editor")} + tooltipText={t("Editor")}web/src/beta/features/PluginPlayground/Plugins/presets/viewerAndSceneSettings/mouseEvent.ts (1)
20-90
: Interactive coordinate display implementationThe widget implementation provides a clean UI for displaying coordinates with good styling and structure. The communication between the viewer and UI is properly handled with event listeners and message passing.
A few minor suggestions to enhance the code:
- <div>Latitude: <span id="lat" class="coordinate-value">-</span>°</div> - <div>Longitude: <span id="lng" class="coordinate-value">-</span>°</div> - <div>Height: <span id="height" class="coordinate-value">-</span> m</div> + <div>Latitude: <span id="lat" class="coordinate-value">-</span><span aria-hidden="true">°</span></div> + <div>Longitude: <span id="lng" class="coordinate-value">-</span><span aria-hidden="true">°</span></div> + <div>Height: <span id="height" class="coordinate-value">-</span><span aria-hidden="true"> m</span></div>
- Consider separating the units from the values with aria-hidden to improve screen reader experience
- Consider adding aria-live="polite" to the coordinate container to announce changes to screen readers
server/internal/usecase/interactor/scene.go (1)
699-701
: Consider handling url.Parse errors.
Currently, the error fromurl.Parse(urlStr)
is ignored, which could lead to passing an invalid path toAddZipAsset
.server/internal/usecase/interfaces/asset.go (1)
41-41
: Add documentation for the Update method.
Consider adding a comment explaining what the method does with the optionalProjectID
.server/e2e/gql_project_export_import_test.go (1)
122-141
: Consider adding error checks for an empty response in exporProject.
If the response is empty, the file might be invalid. A simple check helps detect issues early.server/internal/adapter/gql/resolver_mutation_project.go (1)
178-182
: Remove commented-out code
Please remove the commented-out lines for clarity since they are no longer used.- // pid2, err := gqlmodel.ToID[id.Project](gqlmodel.ID(prj.ID().String())) - // if err != nil { - // return nil, err - // }server/e2e/gql_storytelling_test.go (2)
15-55
: Well-structured end-to-end coverage for basic Story CRUD.
This block of code thoroughly tests creating, fetching, updating, and deleting stories, including verifying new fieldsenableGa
andtrackingId
. As a minor enhancement, consider adding an assertion to confirm that the updated story title ("test2") is reflected in the fetched result.
231-413
: Thorough story publishing test.
The generated JSON is verified in-depth (including default GA fields), ensuring your published story matches expectations. The test is somewhat extensive; if it grows further, consider splitting parts of it into separate tests for improved readability.server/e2e/seeder.go (2)
265-265
: Clarify the index parameter usage.
Using-1
as a position index for widget insertion might be confusing to new contributors. Consider documenting that negative indices imply an append operation.
287-293
: Consider setting default GA parameters during the seeding process.
If you want seeded stories to include GA settings by default, add calls to.EnableGa(...)
and.TrackingId(...)
when building the story. Otherwise, they remain uninitialized in this seed data.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
server/e2e/test.zip
is excluded by!**/*.zip
📒 Files selected for processing (81)
server/e2e/common.go
(7 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_project_export_import_test.go
(3 hunks)server/e2e/gql_project_export_test.go
(0 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_scene_test.go
(4 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/gql_user_test.go
(2 hunks)server/e2e/gql_validate_geojson_test.go
(1 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/e2e/seeder.go
(5 hunks)server/go.mod
(1 hunks)server/gql/asset.graphql
(5 hunks)server/gql/storytelling.graphql
(3 hunks)server/gql/workspace.graphql
(2 hunks)server/gqlgen.yml
(0 hunks)server/internal/adapter/gql/generated.go
(61 hunks)server/internal/adapter/gql/gqlmodel/convert_asset.go
(1 hunks)server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go
(5 hunks)server/internal/adapter/gql/gqlmodel/convert_project.go
(2 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(5 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
(6 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 hunks)server/internal/adapter/gql/resolver_query.go
(1 hunks)server/internal/adapter/gql/resolver_team.go
(1 hunks)server/internal/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/fs/file.go
(2 hunks)server/internal/infrastructure/fs/file_test.go
(3 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/infrastructure/mongo/mongodoc/scene.go
(0 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/asset.go
(6 hunks)server/internal/usecase/interactor/nlslayer.go
(3 hunks)server/internal/usecase/interactor/project.go
(5 hunks)server/internal/usecase/interactor/project_test.go
(0 hunks)server/internal/usecase/interactor/scene.go
(5 hunks)server/internal/usecase/interactor/storytelling.go
(3 hunks)server/internal/usecase/interactor/storytelling_test.go
(1 hunks)server/internal/usecase/interfaces/asset.go
(2 hunks)server/internal/usecase/interfaces/project.go
(2 hunks)server/internal/usecase/interfaces/story.go
(1 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/nlslayer/feature.go
(0 hunks)server/pkg/nlslayer/feature_test.go
(1 hunks)server/pkg/scene/builder/story.go
(1 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)web/src/beta/features/Dashboard/ContentsContainer/Projects/Project/hooks.tsx
(1 hunks)web/src/beta/features/Dashboard/ContentsContainer/Projects/index.tsx
(1 hunks)web/src/beta/features/Dashboard/index.tsx
(1 hunks)web/src/beta/features/Editor/Map/InspectorPanel/LayerInspector/DataSource/SketchCustomProperties/CustomPropertyFieldItem.tsx
(1 hunks)web/src/beta/features/Editor/Map/InspectorPanel/LayerInspector/index.tsx
(6 hunks)web/src/beta/features/Editor/Map/LayerStylePanel/Editor/StyleInterface/index.tsx
(3 hunks)web/src/beta/features/Editor/Map/LayerStylePanel/PresetLayerStyle/index.tsx
(3 hunks)web/src/beta/features/Editor/Map/LayerStylePanel/index.tsx
(1 hunks)web/src/beta/features/Editor/Map/LayersPanel/LayerItem.tsx
(3 hunks)web/src/beta/features/Editor/Map/ToolsPanel/index.tsx
(3 hunks)web/src/beta/features/Navbar/LeftSection/index.tsx
(1 hunks)web/src/beta/features/PluginPlayground/Plugins/presets/index.ts
(2 hunks)web/src/beta/features/PluginPlayground/Plugins/presets/viewerAndSceneSettings/mouseEvent.ts
(1 hunks)web/src/beta/lib/reearth-ui/components/Icon/index.tsx
(3 hunks)web/src/beta/lib/reearth-ui/components/IconButton/index.tsx
(4 hunks)web/src/beta/lib/reearth-ui/components/Popup/hooks.ts
(4 hunks)web/src/beta/lib/reearth-ui/components/Popup/index.tsx
(5 hunks)web/src/beta/lib/reearth-ui/components/Tabs/index.tsx
(4 hunks)web/src/beta/lib/reearth-ui/components/Tooltip/index.tsx
(3 hunks)web/src/beta/ui/components/EntryItem/index.tsx
(3 hunks)web/src/beta/ui/components/ManagerBase/ManagerHeader.tsx
(1 hunks)web/src/services/i18n/translations/en.yml
(6 hunks)web/src/services/i18n/translations/ja.yml
(6 hunks)
💤 Files with no reviewable changes (5)
- server/gqlgen.yml
- server/internal/infrastructure/mongo/mongodoc/scene.go
- server/pkg/nlslayer/feature.go
- server/internal/usecase/interactor/project_test.go
- server/e2e/gql_project_export_test.go
✅ Files skipped from review due to trivial changes (3)
- web/src/beta/features/Dashboard/ContentsContainer/Projects/Project/hooks.tsx
- web/src/beta/features/Dashboard/index.tsx
- web/src/beta/features/Dashboard/ContentsContainer/Projects/index.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
- server/e2e/mock_test.go
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/e2e/gql_me_test.go
- server/pkg/storytelling/story_bulider.go
- server/internal/usecase/interfaces/story.go
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/internal/usecase/interactor/storytelling_test.go
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/e2e/gql_project_test.go
- server/pkg/storytelling/story.go
- server/gql/storytelling.graphql
- server/internal/usecase/interactor/storytelling.go
- server/internal/adapter/gql/gqlmodel/models_gen.go
- server/e2e/common.go
🧰 Additional context used
🧠 Learnings (2)
server/internal/adapter/gql/resolver_mutation_project.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1151
File: server/internal/adapter/gql/resolver_mutation_project.go:0-0
Timestamp: 2024-11-12T15:21:04.150Z
Learning: In the project export functionality, the ZIP file is deleted after saving to storage.
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`.
server/e2e/gql_project_export_import_test.go (2)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:48-80
Timestamp: 2024-11-12T15:21:04.151Z
Learning: In `server/e2e/gql_import_export_test.go`, test functions like `TestCallImportProject` may have disabled assertions due to problems with GraphQL.
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1141
File: server/e2e/gql_import_export_test.go:12-44
Timestamp: 2024-11-12T15:21:04.150Z
Learning: The response body assertions in `TestCallExportProject` are disabled due to existing problems with GraphQL.
🔇 Additional comments (206)
server/pkg/scene/builder/story.go (1)
45-45
: Fixed missing Title field in storyJSON serialization.The change correctly adds the Title field to the storyJSON object returned by the method. This ensures the story title is properly included in the JSON output, which is consistent with how other title fields are handled in the codebase (like in pageJSON).
web/src/beta/lib/reearth-ui/components/Popup/hooks.ts (6)
11-12
: Enhanced Popup with arrow functionality.The import of the
arrow
function from@floating-ui/react
is appropriate for adding visual arrow indicators to the popup.
14-14
: Proper import of useRef for the arrow element.The addition of
useRef
import is necessary for creating a reference to the arrow DOM element.
29-29
: Arrow reference properly initialized.The
arrowRef
is correctly initialized as null and will be populated when the component renders.
55-56
: Arrow middleware correctly implemented.The arrow middleware is properly added to the floating UI configuration, which will handle the positioning of the arrow element relative to the popup and its anchor.
75-75
: Arrow reference properly exposed in the hook's return value.Exposing the
arrowRef
in the returned object makes it available to the component using this hook, allowing it to properly connect the DOM element to the ref.
79-79
: Dependencies array is correctly maintained.The
arrowRef
is intentionally omitted from the useMemo dependencies array since the ref object itself is stable (only its.current
property changes), which is the correct implementation.web/src/services/i18n/translations/en.yml (6)
151-151
: Verify "Layer Style" Key Usage
The "Layer Style:" key is introduced with an empty value. Confirm that this entry is consistent with similar UI elements across the application and that a meaningful default (or translation note) is provided where needed.
161-162
: Check "Fly to" and "Visibility" Keys
The new keys "Fly to:" and "Visibility:" are added with empty strings. Please ensure that their default values will be set at runtime or later updated by translators to meet UI expectations.
169-173
: Review "Style" Keys for Map Elements
A set of style-related keys ("Marker style:", "Polyline style:", "Polygon style:", "3dtiles style:", and "3D model style:") have been added with empty default values. Verify that these keys are either populated dynamically or will receive proper default translations to maintain UI consistency.
183-184
: Confirm "Assign style" and "New style" Keys
The keys "Assign style:" and "New style:" have been introduced with empty placeholders. Ensure these entries integrate seamlessly with the rest of the style management UI and consider providing default text if appropriate.
259-260
: New UI Module Keys Added
The additions of "Dashboard:" and "Editor:" are noticeable. Although their default values are empty, please verify that the UI or localization process supplies the proper texts for these modules to avoid display issues.
392-393
: Evaluate Layout Keys
The new "Grid Layout:" and "List Layout:" keys are added with empty values. Confirm that the intended default strings will be provided elsewhere or update these entries to meet UI feedback for layout selections.web/src/services/i18n/translations/ja.yml (7)
133-133
: "More" Key Translation Confirmation
The "More:" key now has the Japanese translation "その他". Please verify that this translation fits the context in which it's used and is consistent with the overall tone of the interface.
152-155
: Review Translation Consistency for Layer Details
The entries for "Layer:", "Feature:", "Layer Style:" (translated as "レイヤースタイル"), and "Infobox:" (translated as "インフォボックス") have been localized. Ensure these translations are contextually appropriate and consistent with similar terms elsewhere in the application.
164-165
: Verify "Fly to" and "Visibility" Translations
The new keys "Fly to:" and "Visibility:" are localized as "移動" and "表示・非表示切り替え" respectively. Please double-check that these translations accurately reflect the intended UI actions and terminology.
172-176
: Check Translated Style Keys
The style keys "Marker style:", "Polyline style:", "Polygon style:", "3dtiles style:", and "3D model style:" have been translated as "マーカースタイル", "ポリラインスタイル", "ポリゴンスタイル", "3Dタイルスタイル", and "3Dモデルのスタイル". Verify that these translations align with the product’s design terminology and are consistently used in related components.
186-187
: Confirm "Assign style" and "New style" Translations
The keys "Assign style:" and "New style:" are now rendered as "スタイルの割り当て" and "新しいスタイル" respectively. Please check for consistency in language tone and ensure they meet the overall style guidelines of the application.
262-263
: Dashboard and Editor Key Translations
The newly added "Dashboard:" and "Editor:" entries are translated as "ダッシュボード" and "エディタ". Confirm that these translations are accurate and consistently applied throughout the UI.
395-396
: Validate Layout Key Translations
The "Grid Layout:" and "List Layout:" keys are localized as "グリッドレイアウト" and "リストのレイアウト". Ensure that these translations effectively convey the intended layout options and are in line with other parts of the interface.server/internal/adapter/gql/gqlmodel/convert_nlslayer_test.go (5)
11-13
: Method signature update aligned with package changesThe change from implicit to explicit feature ID generation is consistent with the removal of
NewFeatureWithNewId
and reflects the new pattern of creating features with explicitly generated IDs.
21-23
: Consistent implementation of new feature creation patternThe explicit ID generation approach is consistently applied across all feature creation calls.
31-33
: Consistent implementation of new feature creation patternThe explicit ID generation approach is consistently applied for polygon features as well.
41-43
: Consistent implementation of new feature creation patternThe explicit ID generation approach is consistently applied for multi-polygon features.
51-53
: Consistent implementation of new feature creation patternThe explicit ID generation approach is consistently applied for geometry collection features.
server/internal/usecase/interactor/nlslayer.go (3)
893-895
: Method signature update aligned with package changesThe change from implicit to explicit feature ID generation in
AddGeoJSONFeature
follows the new pattern implemented across the codebase, enhancing clarity around ID generation.
1154-1159
: Method signature update aligned with package changesThe change in
ImportNLSLayers
ensures consistent implementation of the new feature creation pattern with explicit ID generation.
1162-1164
:✅ Verification successful
Verify feature property handling
The feature properties handling following the new creation pattern is properly implemented, but should be verified with existing clients of this functionality.
Run the following script to check if any code relies on the old feature creation pattern:
🏁 Script executed:
#!/bin/bash # Search for any remaining instances of NewFeatureWithNewId rg "NewFeatureWithNewId" --type goLength of output: 115
Verified: New Feature Property Handling is Confirmed
The new pattern for handling feature properties is correctly implemented. The search for
NewFeatureWithNewId
only returned a reference in the test file (server/pkg/nlslayer/feature_test.go
), which suggests that no production code is relying on the old creation pattern. Please ensure that the tests accurately reflect the intended behavior, but no further modifications seem necessary.web/src/beta/features/Editor/Map/LayerStylePanel/index.tsx (1)
75-76
: Good enhancement with tooltip!Adding a tooltip to the "Assign style" button improves user experience by making the button's function more clear.
web/src/beta/ui/components/ManagerBase/ManagerHeader.tsx (2)
189-189
: Great UX improvement with tooltip!Adding the tooltipText property for the grid layout button enhances user experience by clarifying the button's purpose.
196-196
: Consistent tooltip implementation!Adding tooltipText for the list layout button maintains UI consistency and improves usability.
web/src/beta/features/Editor/Map/InspectorPanel/LayerInspector/DataSource/SketchCustomProperties/CustomPropertyFieldItem.tsx (1)
62-62
: Good UX enhancement with tooltip!Adding tooltipText to the options button improves discoverability and clarifies the button's purpose.
web/src/beta/features/Navbar/LeftSection/index.tsx (1)
66-71
: Great navigation enhancement!Adding tooltipText to the Dashboard button improves usability by making the navigation purpose clear.
web/src/beta/ui/components/EntryItem/index.tsx (3)
8-8
: Internationalization support addedGood addition of the i18n service import to support localization of UI elements.
46-46
: Added translation supportProperly implemented the
useT
hook to enable internationalization support within the component.
101-102
: Enhanced user experience with tooltipGreat addition of tooltip text and placement for the "More" button, which improves accessibility and user experience by providing context for the action.
web/src/beta/features/PluginPlayground/Plugins/presets/index.ts (2)
31-36
: Added new UI component importsGood organization of imports, keeping related UI components together and adding the new mouseEvents import in the appropriate section with other viewer settings.
67-73
: Added mouseEvents plugin to viewer settingsWell-structured addition of the mouseEvents plugin to the presetPlugins array, maintaining consistent formatting with the existing code by using a multi-line array.
web/src/beta/features/PluginPlayground/Plugins/presets/viewerAndSceneSettings/mouseEvent.ts (3)
1-18
: Well-structured plugin configuration fileThe plugin metadata is well-organized with appropriate ID, name, and version information. The extension configuration correctly specifies the widget type.
78-88
: Proper event handling in viewerThe click event handler correctly captures and extracts coordinates, and the message structure is clear and consistent.
92-96
: Clean plugin exportThe plugin export is well-structured with a clear ID, title, and files array.
web/src/beta/features/Editor/Map/LayersPanel/LayerItem.tsx (3)
95-96
: Enhanced accessibility with tooltipsGood addition of tooltip text for the "Fly to" action, improving user experience by providing context for the button's function.
108-109
: Improved UI with visibility tooltipWell-implemented tooltip for the visibility toggle button, maintaining consistency with other tooltips in the application.
117-124
: Updated dependency array for useMemoProperly updated the dependency array to include the newly added dependencies (
editingLayerNameId
andt
), ensuring the memoized value is recalculated when these values change.web/src/beta/features/Editor/Map/LayerStylePanel/Editor/StyleInterface/index.tsx (4)
3-3
: Good internationalization implementation.The useT hook is correctly imported to support localization of UI elements.
34-34
: LGTM!Properly initializing the translation function from the useT hook.
108-117
: Well-structured translation mapping for appearance types.Good implementation of a memoized object that maps each appearance type to its translated label. Using useMemo with the translation function as a dependency ensures the mapping is updated when the language changes.
119-140
: Enhanced tabs with proper tooltips and internationalization.The appearance type tabs now correctly include tooltips with translated text, improving the user experience for non-English speakers. The structure with tooltip placement is consistent with best practices.
web/src/beta/features/Editor/Map/LayerStylePanel/PresetLayerStyle/index.tsx (3)
7-7
: LGTM for i18n import.The useT hook is correctly imported to support localization of UI elements.
38-38
: LGTM!Properly initializing the translation function.
152-159
: Enhanced button with accessible tooltip.Good implementation of tooltips for the "New style" button, improving user experience and accessibility through proper internationalization.
web/src/beta/lib/reearth-ui/components/IconButton/index.tsx (4)
4-4
: LGTM!Updated import to include IconProps type which is necessary for the tooltip functionality.
18-18
: Good type extension for tooltip support.Properly extending IconButtonProps with tooltip-related properties from IconProps using Pick.
30-33
: LGTM for prop destructuring.Added the new tooltip-related props to the component's destructuring pattern.
55-61
: Well-implemented tooltip integration.The Icon component now correctly receives the tooltip-related props with a consistent offset, enabling proper tooltip display across the application.
web/src/beta/features/Editor/Map/InspectorPanel/LayerInspector/index.tsx (4)
10-10
: LGTM for i18n import.The useT hook is correctly imported to support localization.
50-50
: LGTM!Properly initializing the translation function.
91-93
: Good tooltip implementation with consistent placement.Each tab now has properly translated tooltips with consistent left placement, improving the user experience and accessibility for non-English users.
Also applies to: 100-102, 122-124, 137-139
147-162
: Updated dependency array with translation function.The dependencies array now correctly includes the translation function and has been reorganized for better readability and maintenance.
web/src/beta/lib/reearth-ui/components/Tooltip/index.tsx (7)
16-18
: Good addition of customization props to improve tooltip flexibility.The addition of
offset
,iconColor
, andplacement
props provides better customization options for the Tooltip component, making it more versatile and reusable across different contexts.
21-28
: Well-structured prop destructuring with default values.Good implementation of prop destructuring with a sensible default for the offset (6). This makes the component more user-friendly by providing reasonable defaults while allowing for customization.
49-51
: Props are correctly passed to the Popup component.The new
offset
andplacement
props are properly forwarded to the Popup component, ensuring proper positioning of tooltips.
54-59
: Good conditional styling based on tooltip type.The icon color is now properly conditional based on whether it's an experimental tooltip (using theme.warning.weakest) or a custom tooltip (using the provided iconColor). This improves visual consistency.
69-76
: Consistent conditional styling for Typography component.The Typography color is appropriately conditioned on the tooltip type, matching the icon styling behavior and providing consistent visual feedback.
1-1
:✅ Verification successful
Ensure correct import is used for Placement type.
The code imports
Placement
type from@floating-ui/react
. This is necessary for the new placement prop, but ensure this is the correct package and interface according to your project dependencies.
🏁 Script executed:
#!/bin/bash # Verify if @floating-ui/react is a dependency and how it's used elsewhere grep -r "@floating-ui/react" --include="package.json" . grep -r "import.*Placement.*from \"@floating-ui/react\"" --include="*.tsx" --include="*.ts" .Length of output: 517
Verified: Correct import for Placement
The search results confirm that
@floating-ui/react
is correctly listed as a dependency (version 0.24.7) and used consistently in several files includingTooltip/index.tsx
. No changes are needed here.
66-67
:✅ Verification successful
Added 'tooltip' prop to Popup component.
Ensure this property is defined in the Popup component to properly handle tooltip-specific styling.
🏁 Script executed:
#!/bin/bash # Check if Popup component has the tooltip prop grep -A 15 "export type PopupProps" web/src/beta/lib/reearth-ui/components/Popup/index.tsxLength of output: 482
Popup tooltip prop verified
Thetooltip
property is correctly defined in thePopupProps
type inweb/src/beta/lib/reearth-ui/components/Popup/index.tsx
, so no further modifications are needed.web/src/beta/lib/reearth-ui/components/Icon/index.tsx (5)
1-2
: Appropriate imports for new functionality.The import of
Placement
from@floating-ui/react
andTooltip
from the project's UI library are necessary for the new tooltip functionality being added to the Icon component.
16-18
: Good addition of tooltip-related properties to IconProps.The addition of
tooltipText
,offset
, andplacement
properties to the IconProps type allows for enhanced tooltip functionality, making the Icon component more versatile.
27-29
: Props properly destructured for use.The new props are correctly destructured in the component function signature, maintaining consistency with the existing code style.
48-56
: Good refactoring to extract Icon rendering logic.The refactoring to extract the Icon rendering into an
IconComponent
variable improves code readability and makes the conditional rendering based on tooltip presence cleaner.
57-68
: Well-implemented conditional rendering with tooltip.The conditional rendering logic based on the presence of tooltipText is clean and effective. When tooltipText is provided, the Icon is wrapped in a Tooltip component with the appropriate props passed through.
web/src/beta/lib/reearth-ui/components/Popup/index.tsx (6)
7-8
: New imports for enhanced functionality.The addition of
ShiftOptions
andFloatingArrow
imports from@floating-ui/react
supports the new tooltip arrow functionality in the Popup component.
11-11
: Updated import for theming.The import has been expanded to include
useTheme
in addition tostyled
, which is necessary for accessing theme properties in the component.
67-107
: Enhanced Content component with tooltip arrow support.The Content component has been significantly enhanced to support rendering a tooltip arrow with proper theming. The changes include:
- Updated type definition to include the tooltip prop
- Accessing theme with useTheme
- Conditional rendering of FloatingArrow when tooltip is true
- Proper styling and positioning of the arrow using theme colors
This implementation follows best practices for conditional rendering and theming.
122-122
: Added tooltip property to PopupProps type.This correctly extends the component's API to support the new tooltip functionality.
134-134
: Destructuring tooltip prop in Popup component.The tooltip prop is properly destructured from the rest of the options, indicating it's handled separately from the props passed to the usePopover hook.
176-176
: Correctly passing tooltip prop to Content component.The tooltip prop is properly passed down to the Content component where it's used to conditionally render the FloatingArrow.
web/src/beta/lib/reearth-ui/components/Tabs/index.tsx (4)
1-6
: Updated imports to include IconProps.The import list has been updated to include IconProps, which is needed for the type extension of TabItem.
22-22
: Well-implemented type extension using Pick utility.The TabItem type is extended with specific properties from IconProps using the Pick utility type. This is a clean and type-safe way to reuse types without duplication.
86-86
: Updated destructuring to include new properties.The destructuring in the map function is updated to include the new tooltipText and placement properties, ensuring they're available for use in the rendered components.
96-98
: Properly passing tooltip props to Icon component.The tooltipText and placement props are correctly passed to the Icon component, enabling tooltip functionality for tab icons.
web/src/beta/features/Editor/Map/ToolsPanel/index.tsx (7)
1-1
: Good addition of Placement import for tooltip positioning.The import of
Placement
from "@floating-ui/react" enables proper typing for the tooltip placement property, ensuring type safety when specifying tooltip positions.
4-4
: Nice addition of i18n support.Adding the
useT
hook enables proper internationalization of the tooltip texts, making the UI more accessible to users of different languages.
13-14
: Good enhancement of the SketchTool type.The addition of
tooltipText
and optionalplacement
properties to the SketchTool type properly reflects the enhanced functionality while maintaining type safety.
20-20
: Appropriate use of translation hook.The
useT
hook is correctly initialized at the component level to be used for all tooltip translations.
26-27
: Well-structured tooltip implementation across all tools.All sketch tools consistently include both tooltip text (properly translated) and placement properties. The tooltip texts are concise and descriptive, enhancing usability without cluttering the UI.
Also applies to: 33-34, 40-41, 47-48, 54-55, 61-62, 68-69, 75-76
80-80
: Correctly updated dependency array.The useMemo dependency array has been properly updated to include the
t
function, ensuring the sketch tools array is recalculated when the language changes.
90-103
: Clean implementation of tooltip properties in the IconButton components.The destructuring in the map function and passing of tooltip properties to IconButton components is well-implemented. The code remains readable while adding the new functionality.
server/pkg/asset/builder.go (1)
57-60
: Clean implementation of Project method following the builder pattern.The new
Project
method follows the consistent builder pattern seen throughout the codebase, properly setting the project field and returning the builder for method chaining.server/internal/adapter/gql/gqlmodel/convert_asset.go (2)
12-16
: Project ID handling logic looks correct.The implementation properly handles the null case for project fields and follows existing patterns for ID conversion.
22-22
: Correctly integrating ProjectID in returned Asset struct.The ProjectID field is properly added to the returned Asset struct, maintaining consistency with the domain model changes.
server/pkg/asset/asset.go (2)
18-18
: Asset struct properly extended with project field.The new
project
field is added as a pointer type, which is appropriate as it appears to be an optional association.
34-40
: Clean getter and setter methods for project field.The implementation follows the existing pattern for getters and setters in this file. Both methods are simple and focused on their specific responsibilities.
server/e2e/gql_user_test.go (1)
28-28
: Unused gateway.File parameter in baseSeederUser function.The function signature has been updated to include a
gateway.File
parameter, but this parameter is not used anywhere in the function implementation.#!/bin/bash # Search for potential usage of the File parameter within the function ast-grep --pattern 'func baseSeederUser($_, $_, $f) { $$$ $f.$$$ $$$ }'server/go.mod (1)
133-133
: Package moved to indirect dependenciesThe
github.com/oklog/ulid
package has been moved from direct dependencies to indirect dependencies, indicating it's no longer directly imported by the codebase but is required by another dependency.server/e2e/gql_validate_geojson_test.go (1)
186-186
:✅ Verification successful
Updated function call with new projectId parameter
The
createAssetFromFileData
function call has been updated to include anil
value for the newprojectId
parameter. This change aligns with the broader refactoring to support project associations in assets.
🏁 Script executed:
#!/bin/bash # Check the function signature in the codebase rg -n "func createAssetFromFileData" --type goLength of output: 235
Verified: Updated Function Call Includes New
projectId
Parameter
- The function signature in
server/e2e/gql_asset_test.go
now accepts a newprojectId
parameter (*string
), confirming that the update is correct.- The test in
server/e2e/gql_validate_geojson_test.go
(line 186) correctly passesnil
forprojectId
, aligning it with the refactoring changes made to support project associations.The changes have been verified and are approved.
server/pkg/asset/id.go (1)
10-10
:✅ Verification successful
Added ProjectID type and related functions
Good additions of ProjectID type and helper functions. These follow the same pattern as the existing ID and WorkspaceID types in the file, maintaining consistency.
Also applies to: 14-14, 18-18, 22-22, 26-26
🏁 Script executed:
#!/bin/bash # Verify where these ProjectID types are used to understand their integration rg "ProjectID.*asset" --type goLength of output: 1199
Verification Complete: ProjectID type integration approved
The new
ProjectID
alias is consistently implemented following the same pattern as existing ID types. Our search confirms that it’s correctly used throughout various components (e.g., inserver/internal/infrastructure/memory/asset.go
,server/internal/infrastructure/mongo/asset.go
, and other asset-related files). There are no outstanding issues with this addition.server/internal/infrastructure/fs/file_test.go (3)
116-118
: Base URL for file repository changedThe base URL pattern for the file repository has been updated, moving from an assets-specific path (
/assets
) to the root path (/
). This change simplifies the URL structure.
165-165
: Base URL for file repository changed from assets path to rootConsistent with the earlier change, the base URL has been updated from
https://example.com/assets
tohttps://example.com/
.
316-317
:✅ Verification successful
Base URL for URL parsing updated to root path
Consistent with the other URL changes in this file, the base URL for parsing has been updated from
http://hoge.com/assets
tohttp://hoge.com/
.
🏁 Script executed:
#!/bin/bash # Check how asset URLs are constructed in the file.go implementation rg "getAssetFileURL" --type go -A 5 -B 5Length of output: 3206
Confirmed: Base URL Update for URL Parsing
The test now correctly parses the base URL as
"http://hoge.com/"
—which is then used bygetAssetFileURL
to construct the full asset URL. The updated test snippet:e, err := url.Parse("http://hoge.com/assets/xxx.yyy") assert.NoError(t, err) b, err := url.Parse("http://hoge.com/") assert.NoError(t, err) assert.Equal(t, e, getAssetFileURL(b, "xxx.yyy"))accurately reflects the change, and the surrounding code in
file.go
corroborates that asset URLs are built by appending asset paths to the provided base. No further changes are needed.server/internal/usecase/repo/asset.go (1)
20-20
: Signature change aligns with project context integration.The change from
FindByWorkspace
toFindByWorkspaceProject
with the additional*id.ProjectID
parameter enhances the asset repository interface to support project-specific asset retrieval. This change is consistent with the broader pattern of associating assets with specific projects rather than just workspaces.server/gql/asset.graphql (6)
5-5
: Added project association to Asset type.Adding the
projectId
field to the Asset type is a good enhancement that will allow clients to understand which project an asset belongs to.
24-24
: Project association during asset creation.Adding the optional
projectId
field to theCreateAssetInput
type allows clients to associate assets with specific projects during creation.
29-32
: New UpdateAssetInput type for asset updates.The new
UpdateAssetInput
type provides a clean interface for updating an asset's project association. The structure follows the GraphQL convention with an ID field for the entity to update and fields representing updatable properties.
49-52
: New UpdateAssetPayload for mutation response.The
UpdateAssetPayload
type follows GraphQL best practices by returning the updated entity's ID and the modified field(s). This allows clients to track the changes applied.
75-75
: Project filtering for assets query.Adding the optional
projectId
parameter to theassets
query enhances the API by allowing clients to filter assets by project, providing more targeted data retrieval.
84-84
: New updateAsset mutation.The
updateAsset
mutation completes the CRUD operations for assets by providing the ability to update an asset's project association after creation.server/internal/adapter/gql/gqlmodel/convert_project.go (3)
108-113
: New ProjectExport struct for project data export.The
ProjectExport
struct provides a clean data structure for exporting project data, including essential metadata like name, description, and image URL.
115-125
: Conversion function for project export.The
ToProjectExport
function properly handles nil input and maps project properties to the export structure.
127-137
: Consider improving error handling and JSON conversion.
- Using
json.MarshalIndent
for conversion is unnecessary and less efficient thanjson.Marshal
.- Silently returning nil on errors makes debugging harder.
Apply this diff to improve the implementation:
func ToProjectExportFromJSON(data map[string]any) *ProjectExport { var p ProjectExport - bytes, err := json.MarshalIndent(data, "", " ") + bytes, err := json.Marshal(data) if err != nil { - return nil + return nil // Consider logging the error or returning it } if err := json.Unmarshal(bytes, &p); err != nil { - return nil + return nil // Consider logging the error or returning it } return &p }server/e2e/gql_scene_test.go (2)
16-16
: Object navigation improvement in tests.The changes to use structured object navigation (
r.Object().Value()
instead ofr.Value()
) improve test clarity and consistency. This approach is more explicit about traversing the response object hierarchy.Also applies to: 42-42, 71-73
77-100
: New helper function for test project creation.The
createProjectWithExternalImage
function is a valuable addition that encapsulates the logic for creating a project with an external image URL in tests. This promotes code reuse across test files.server/internal/app/auth_client.go (2)
105-107
: LGTM: Reduced noise in logging for e2e tests.This is a good optimization to suppress verbose logging for e2e test users, making test output cleaner and easier to read.
115-117
: LGTM: Consistent log filtering for e2e test users.This change consistently applies the same log suppression logic for both user and operator details, ensuring clean test output.
server/internal/infrastructure/memory/asset.go (2)
49-49
: Enhancement to asset filtering by adding project support.The function has been renamed from
FindByWorkspace
toFindByWorkspaceProject
and now accepts a project ID parameter, allowing for more granular asset filtering.
55-58
: LGTM: Proper implementation of project-based asset filtering.The implementation correctly handles both cases - when a project ID is provided and when it's not, maintaining backward compatibility while adding new functionality.
server/internal/infrastructure/fs/file.go (2)
113-113
: Path validation strengthened for RemoveAsset.The path validation now correctly checks against the full asset directory path rather than just the base URL path, improving security by ensuring files can only be removed from the designated asset directory.
286-290
: URL generation improved for root path handling.This change adds a special case for root path handling, ensuring proper URL construction when the base path is at the root level. This prevents potential issues with duplicate slashes or incorrect path joining.
server/internal/adapter/gql/resolver_query.go (1)
15-16
: Fix inconsistent function name.The loader call uses
FindByWorkspace
but the implementation usesFindByWorkspaceProject
.Apply this diff to fix the inconsistency:
- return loaders(ctx).Asset.FindByWorkspace(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination) + return loaders(ctx).Asset.FindByWorkspaceProject(ctx, teamID, projectId, keyword, gqlmodel.AssetSortTypeFrom(sortType), pagination)server/internal/adapter/gql/resolver_mutation_asset.go (2)
19-26
: LGTM: CreateAsset now handles optional ProjectID wellThe refactoring to handle an optional ProjectID is clean and follows good practices with proper error handling.
41-62
: New UpdateAsset method looks goodThe implementation correctly handles conversion between GraphQL and domain types, with appropriate error handling and use case delegation.
server/e2e/ping_test.go (1)
9-9
: Code simplification approvedThe test has been simplified by replacing explicit server configuration with a call to
ServerPingTest(t)
, which makes the test more maintainable.server/internal/app/app.go (2)
72-78
: Auth middleware handling improvedThe conditional handling of authentication middleware now properly supports disabled auth services, making the code more flexible.
164-168
: AuthMiddlewareDummy implementation is cleanThe dummy middleware implementation is simple and correctly implements the expected interface by passing requests directly to the next handler.
server/internal/usecase/interfaces/project.go (1)
74-75
: Method renaming and type safety improvementsThe interface methods have been renamed from
ExportProject
toExportProjectData
andImportProject
toImportProjectData
for clarity. Additionally, theImportProjectData
method now uses the more type-safeidx.ID[accountdomain.Workspace]
instead of a string for the workspace ID parameter.server/internal/adapter/gql/resolver_team.go (1)
27-28
:❓ Verification inconclusive
Function signature updated - ensure param consistency across resolvers
The function signature has been updated to include a new parameter
projectID
of type*gqlmodel.ID
, which is now passed to theFindByWorkspace
method of the loader. This aligns with changes in other resolver files to support filtering assets by project.Make sure all places that call this resolver function have been updated to handle the new parameter.
🏁 Script executed:
#!/bin/bash # Check if there are any calls to the Assets method that might be missing the projectID parameter rg --type go "teamResolver\).Assets\(" -A 2 --glob "!*resolver_team.go"Length of output: 73
Resolver Parameter Consistency Verification
The function signature update in
server/internal/adapter/gql/resolver_team.go
was intended to introduce the newprojectID
parameter and pass it to theFindByWorkspace
loader. Our automated grep search (excludingresolver_team.go
) produced no instances of calls toAssets
that might be missing this parameter. However, since the returned output was empty and could indicate either that no external call sites exist or that further inspection is needed, please verify manually that all invocations ofAssets
(outside the resolver file) have been updated accordingly.
- Action Item: Manually inspect the codebase for any calls to
teamResolver.Assets
to ensure that they pass theprojectID
parameter.- Snippet Reference:
func (r *teamResolver) Assets(ctx context.Context, obj *gqlmodel.Team, projectID *gqlmodel.ID, first *int, last *int, after *usecasex.Cursor, before *usecasex.Cursor) (*gqlmodel.AssetConnection, error) { return loaders(ctx).Asset.FindByWorkspace(ctx, obj.ID, projectID, nil, nil, &gqlmodel.Pagination{server/internal/infrastructure/mongo/asset.go (3)
67-81
: Function renamed with enhanced filtering capabilityThe function has been renamed from
FindByWorkspace
toFindByWorkspaceProject
with an additional parameterprojectId
. The implementation now supports filtering assets by either project ID (when provided) or workspace ID (as fallback).This is a good enhancement that allows more granular querying of assets.
84-85
: Filter construction simplifiedThe keyword filter assignment has been simplified to directly assign the regex condition to
filter["name"]
instead of using themongox.And
function, which improves readability.
96-100
: Handle type assertion failure caseThe type assertion to
bson.M
could silently fail if the assertion is unsuccessful. Consider handling the failure case explicitly.Apply this diff to handle the failure case:
- if andFilter, ok := mongox.And(filter, "url", bson.M{ - "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, - }).(bson.M); ok { - filter = andFilter - } + if andResult := mongox.And(filter, "url", bson.M{ + "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, + }); andResult != nil { + if andFilter, ok := andResult.(bson.M); ok { + filter = andFilter + } + }server/internal/infrastructure/mongo/mongodoc/asset.go (5)
17-17
: Added project association to AssetDocumentAdded a new field
Project
to support associating assets with specific projects. This is a good addition that enhances the flexibility of the asset model.
35-41
: Project ID extraction added to NewAsset functionThe function now properly extracts the project ID from the asset and includes it in the document when present. The extraction logic handles the optional nature of the project field correctly.
46-46
: Set Project field in AssetDocument constructorThe Project field is now set properly in the document constructor, completing the implementation of the project association feature.
65-73
: Model conversion for project fieldGood implementation of the project ID conversion in the Model method. The code properly handles the optional nature of the project field and includes appropriate error handling during the conversion process.
78-78
: Include project in asset builderThe project ID is now included in the asset builder, completing the bidirectional conversion between model and document representations.
server/internal/adapter/gql/loader_asset.go (4)
39-39
: Function signature updated to include project IDThe function signature now includes
proId *gqlmodel.ID
parameter to support filtering assets by project. This change aligns with the modifications in other parts of the codebase.
45-53
: Project ID conversion addedThe function now properly converts the optional project ID from the GraphQL model to the internal domain model. The conversion includes appropriate error handling for invalid IDs.
54-54
: Updated function call to use the project-aware implementationReplaced the call to
FindByWorkspace
withFindByWorkspaceProject
to utilize the new functionality. This ensures that asset filtering by project is properly supported.
60-66
: Optimized edge creationThe edge creation code has been optimized by pre-allocating the slice with the exact size needed and directly assigning values instead of using append. This is a small but good performance improvement.
server/internal/usecase/interactor/scene.go (4)
21-21
: No concerns with the import.
668-671
: Guard clause for story retrieval is well-implemented.
No issues here.
728-730
: URL checks are consistent.
No issues here.
753-755
: URL checks are consistent.
No issues here.server/internal/usecase/interfaces/asset.go (3)
28-28
: New ProjectID field is clearly expressed.
No issues here.
39-39
: New method for filtered asset retrieval by project is clear.
No issues here.
43-43
: Revised ImportAssetFiles signature looks consistent.
No issues here.server/e2e/gql_project_export_import_test.go (19)
4-6
: Updated imports are valid and aligned with the new references.Also applies to: 8-8, 12-13
19-19
: Note for test usage is fine.
21-21
: Descriptive test name enhances clarity.
23-23
: Full seeder is used for comprehensive test data.
25-26
: Retrieving seeded data is straightforward.
28-29
: Export procedure invocation is clear.
31-32
: Import procedure call seamlessly follows export.
34-40
: Assertions for presence of keys in the import result are robust.
42-43
: Validating project name and description further ensures correctness.
45-45
: Extracting the new scene ID for subsequent checks is well thought out.
48-48
: Retrieving the newly imported scene for comparison is correct.
50-58
: Comparisons of each section of scene data ensure accuracy.
59-63
: Cleaning up the exported file is good test hygiene.
66-70
: convertLine function is concise and handles line-by-line marshalling neatly.
72-102
: compareValue function robustly checks diffs while ignoring known dynamic fields.
104-111
: isIgnore helper clarifies ignoring certain fields.
113-120
: isID function is specialized but sufficient for widget identifiers.
143-143
: importProject function confirms correct import flow.
No further issues noted.Also applies to: 168-168, 170-170, 172-172
175-175
: getScene function uses GraphQL query effectively for final comparison.Also applies to: 185-185, 187-187, 189-189, 190-190
server/gql/workspace.graphql (3)
8-14
: Addition of projectId parameter enhances asset filtering capability.The addition of the
projectId
parameter to theassets
field allows for filtering assets by their associated project, which aligns with the overall goal of improving component parameterization. This change enables more precise asset querying and management.
15-21
: Code formatting improvement for better readability.Breaking down the parameters into separate lines improves code readability while maintaining the same functionality.
121-123
: Code formatting improvement for better readability.Consistent with other formatting changes, this improves readability of the GraphQL schema.
server/e2e/gql_asset_test.go (10)
15-16
: Updated test function name reflects its specific purpose.Renaming from
TestGetAssets
toTestCoreSupportGetAssets
makes the test's focus more explicit.
17-18
: Simplified test setup using ServerAndRepos.The test now uses a more streamlined setup approach with
ServerAndRepos
rather than manual configuration.
22-76
: Comprehensive asset creation test with JSON structure validation.The test now creates assets with different configurations (PNG/CSV files, with/without core support) and validates the full JSON structure of the response using the new
JSONEqRegexpValue
helper. The inclusion ofprojectId: null
verification is particularly important for testing the new field.
106-107
: Updated repository method to support project filtering.The asset repository's
FindByWorkspaceProject
method now accepts a project ID parameter, aligning with the GraphQL schema changes.
114-115
: Updated assets query to include project filtering capability.The assets retrieval now supports filtering by project ID, which corresponds to the schema changes.
124-179
: Comprehensive test for project-asset associations.This new test thoroughly validates:
- Creating assets associated with specific projects
- Retrieving assets filtered by project
- Moving assets between projects
- Removing project associations
- Asset deletion
The test provides good coverage for the new project association functionality.
181-198
: Updated CreateAsset mutation to support project associations.The mutation now includes
projectId
as an optional parameter, allowing assets to be associated with specific projects during creation.
200-222
: Updated createAsset function to support project associations.The function now accepts a
projectId
parameter, enabling test cases to associate assets with specific projects.
258-285
: Updated createAssetFromFileData to support project associations.This function now accepts a
projectId
parameter, maintaining consistency with thecreateAsset
function.
287-350
: Enhanced getAssets query with project filtering support.The function now accepts a
projectId
parameter and includes it in the GraphQL query, enabling filtering of assets by project. The query structure has been updated to accommodate this new parameter.server/internal/adapter/gql/generated.go (6)
8101-8111
: Google Analytics fields added to Story typeThe PR successfully adds the required Google Analytics fields to the Story type in the GraphQL schema:
enableGa: Boolean!
- Flag to enable/disable Google AnalyticstrackingId: String!
- The GA tracking IDThese additions align with the PR objectives to support Google Analytics integration in the Story component.
8173-8175
: Google Analytics fields added to UpdateStoryInputThe PR correctly adds the Google Analytics fields to the UpdateStoryInput type as nullable fields:
enableGa: Boolean
trackingId: String
Making these fields nullable in the update input is appropriate as it allows for partial updates.
51899-51900
: Handling of new GA fields in unmarshalInputUpdateStoryInputThe code correctly updates the marshaling/unmarshaling logic to handle the new Google Analytics fields in the UpdateStoryInput. The field ordering includes the new fields and the unmarshaling code handles type conversion appropriately.
Also applies to: 52011-52025
61476-61485
: Field resolver implementation for Story GA fieldsThe field resolver implementation for the Story type correctly handles the new Google Analytics fields. The implementation ensures the fields are properly resolved and validates that non-nullable fields are actually present.
6978-6981
: New Asset update functionality addedIn addition to the Google Analytics support, this PR also introduces asset update functionality with:
- New
UpdateAssetInput
withassetId
and optionalprojectId
fields- New
UpdateAssetPayload
return typeThese additions enhance the asset management capabilities of the application.
Also applies to: 6998-7001
41920-42006
:❓ Verification inconclusive
Verify field dependencies in business logic
While the GraphQL schema implementation looks correct, ensure that the business logic handles the relationship between
enableGa
andtrackingId
appropriately.For example, if
enableGa
is false, your application should likely ignore thetrackingId
value. Consider validating in your resolver implementation that whenenableGa
is true, a validtrackingId
is provided.
🏁 Script executed:
#!/bin/bash # Description: Check if the resolver implementation validates tracking ID when GA is enabled # Look for validation logic in story-related files echo "Searching for validation logic in resolver implementations..." rg -A 10 -B 10 "func.*Update.*Story" --type go | rg -A 10 -B 10 "enableGa|trackingId"Length of output: 1735
Attention: Validate GA Flag and Tracking ID in Business Logic
The current generated input unmarshal function (
unmarshalInputUpdateStoryInput
inserver/internal/adapter/gql/generated.go
) parses both theenableGa
andtrackingId
fields without enforcing their logical dependency. Please ensure that:
- If
enableGa
is false, any suppliedtrackingId
is ignored.- If
enableGa
is true, a valid (non-empty and correctly formatted)trackingId
is required.This validation should be handled within your resolver or business logic layer rather than relying solely on the generated input unmarshalling.
server/internal/usecase/interactor/asset.go (2)
88-99
: Validate workspace-to-project consistency
Similar to a previous suggestion, ensure that ifinp.ProjectID
is provided, it belongs to the same workspaceinp.WorkspaceID
.
108-127
: Validate workspace-to-project consistency
Similar to a previous suggestion, ensure the user can also write to the assignedpid
if it's different from the current asset's project.server/internal/adapter/gql/resolver_mutation_project.go (9)
130-130
: Looks good
No concerns for this line.
155-155
: ExportProjectData usage
This call toExportProjectData
is consistent with the new approach.
183-183
: Verify exported asset scope
Here, you passnil
forpid
, which exports all workspace assets instead of just the project's assets. Confirm if that is intended or needed.
189-189
: LGTM
Parsing the asset URL is correct.
214-214
: Handle potential workspace ID conversion error
Previously reported. Properly handle the error fromaccountdomain.WorkspaceIDFrom(string(input.TeamID))
.
224-224
: Handle JSON unmarshal errors
Previously mentioned. Please avoid discarding errors when callingunmarshalProject(tempData)
.
349-349
: Standardize error handling
Check for and handle JSON unmarshal errors when retrievingproject
from the data.
359-359
: Standardize error handling
Check for and handle JSON unmarshal errors when retrievingassets
from the data.
375-375
: Standardize error handling
Check for and handle JSON unmarshal errors when retrievingplugins
andscene
from the data.server/internal/usecase/interactor/project.go (6)
513-513
: Refactored export method usage
No issues with this new function.
526-526
: Approved usage
This call toAddZipAsset
is consistent with the introduced helper method.
566-566
: Add workspace validation
Previously suggested. Validate theworkspace
is not nil or invalid before proceeding.
573-573
: Add project data validation
Previously suggested. Confirm the returnedinput
fromjsonmodel.ToProjectExportFromJSON
is valid.
579-588
: Confirm ignoring imported fields
All imported fields forAlias
,Archived
, andCoreSupport
are overridden with static values. Verify whether this is intentional or if you want to preserve the imported values.
627-649
: Improve error handling
Please see prior suggestions to handle errors more robustly inAddZipAsset
.server/e2e/gql_storytelling_test.go (4)
57-152
: Comprehensive page-level CRUD tests.
The test logic systematically covers creation, duplication, fetching, updating—including error handling—and deletion of pages. Everything appears to be in order, and it provides good coverage of edge cases such as invalid page IDs. Keep it up!
154-207
: Robust block-level CRUD verification.
Creating, moving, and removing blocks is well-structured here. The checks for final block positions after reordering are neat and ensure correctness. No issues found.
208-229
: Effective property update checks for page blocks.
Updating and retrieving block properties with different field types (STRING, SPACING) is well-covered. Everything looks good.
869-904
: Removal of blocks verified correctly.
The final array check withNotConsistsOf(blockID)
neatly ensures the block is indeed removed. This change is well-implemented.server/e2e/seeder.go (2)
121-164
: Revisit asset file validation.
A past review advised adding checks for file size and content type in theaddAsset
function. This can prevent accidental handling of invalid or oversized uploads.
[duplicate_comment, refactor_suggestion_good_to_have]
203-216
: Fix critical error checks.
These lines useerr != err
instead oferr != nil
, causing the condition to never be true. This is a known issue highlighted in previous reviews. Update them as follows to properly handle errors:
[duplicate_comment, critical_issue]-if err = r.Scene.Save(ctx, s); err != err { +if err = r.Scene.Save(ctx, s); err != nil { return err }(Apply similarly in the subsequent checks in lines 205, 207, 209, 211, 213, 215.)
@@ -144,7 +166,7 @@ func (i *Asset) UploadAssetFile(ctx context.Context, name string, zipFile *zip.F | |||
contentType := http.DetectContentType([]byte(zipFile.Name)) |
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.
Use actual file contents for content type detection
Currently, http.DetectContentType([]byte(zipFile.Name))
checks the filename rather than the file contents, leading to inaccurate content type detection.
Apply this fix to read the first 512 bytes of the file:
- contentType := http.DetectContentType([]byte(zipFile.Name))
+ buf := make([]byte, 512)
+ n, err := readCloser.Read(buf)
+ if err != nil && err != io.EOF {
+ return nil, 0, fmt.Errorf("error reading file header: %w", err)
+ }
+ // Reopen file to reset reader position for further operations
+ if cerr := readCloser.Close(); cerr != nil {
+ return nil, 0, fmt.Errorf("error closing file after header read: %w", cerr)
+ }
+ reopened, err := zipFile.Open()
+ if err != nil {
+ return nil, 0, fmt.Errorf("error reopening file: %w", err)
+ }
+ defer reopened.Close()
+ contentType := http.DetectContentType(buf[:n])
📝 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.
contentType := http.DetectContentType([]byte(zipFile.Name)) | |
buf := make([]byte, 512) | |
n, err := readCloser.Read(buf) | |
if err != nil && err != io.EOF { | |
return nil, 0, fmt.Errorf("error reading file header: %w", err) | |
} | |
// Reopen file to reset reader position for further operations | |
if cerr := readCloser.Close(); cerr != nil { | |
return nil, 0, fmt.Errorf("error closing file after header read: %w", cerr) | |
} | |
reopened, err := zipFile.Open() | |
if err != nil { | |
return nil, 0, fmt.Errorf("error reopening file: %w", err) | |
} | |
defer reopened.Close() | |
contentType := http.DetectContentType(buf[:n]) |
07ea4c4
to
47c869e
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: 0
♻️ Duplicate comments (3)
server/e2e/gql_asset_test.go (2)
224-240
: 🛠️ Refactor suggestionAdd error handling for updateAsset response.
The function should check for errors in the GraphQL response.
Apply this diff to add error handling:
func updateAsset(e *httpexpect.Expect, assetId string, projectId *string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "UpdateAsset", Query: `mutation UpdateAsset($assetId: ID!, $projectId: ID) { updateAsset(input: { assetId: $assetId projectId: $projectId }) { assetId projectId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, "projectId": projectId, }, } return Request(e, uID.String(), requestBody) }
242-256
: 🛠️ Refactor suggestionAdd error handling for removeAsset response.
Similar to updateAsset, this function should also check for errors.
Apply this diff:
func removeAsset(e *httpexpect.Expect, assetId string) *httpexpect.Value { requestBody := GraphQLRequest{ OperationName: "RemoveAsset", Query: `mutation RemoveAsset($assetId: ID!) { removeAsset(input: { assetId: $assetId }) { assetId + errors { + message + path + } __typename } }`, Variables: map[string]any{ "assetId": assetId, }, } return Request(e, uID.String(), requestBody) }server/e2e/common.go (1)
266-271
: 🛠️ Refactor suggestionAdd type assertion error handling in JSONEqRegexpValue.
The type assertion in
JSONEqRegexpValue
should handle the case where the assertion fails.Example refactor:
func JSONEqRegexpValue(t *testing.T, actual *httpexpect.Value, expected string) bool { - if actualData, ok := actual.Raw().(map[string]interface{}); ok { + actualData, ok := actual.Raw().(map[string]interface{}) + if !ok { + t.Errorf("expected map[string]interface{}, got %T", actual.Raw()) + return false + } return JSONEqRegexpInterface(t, actualData, expected) - } - return false }
🧹 Nitpick comments (1)
server/e2e/gql_storytelling_test.go (1)
869-869
: Modified block removal assertion syntax.The assertion for block removal now uses
NotConsistsOf
instead of the previous implementation. The change maintains the same test logic but uses a slightly different assertion method.Consider using
NotContains
for simpler and more direct assertion:- res.Object(). - Path("$.data.removeStoryBlock.page.blocks[:].id").Array().NotConsistsOf(blockID) + res.Object(). + Path("$.data.removeStoryBlock.page.blocks[:].id").Array().NotContains(blockID)Also applies to: 900-901
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
server/e2e/common.go
(7 hunks)server/e2e/gql_asset_test.go
(6 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_project_test.go
(6 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/gql/storytelling.graphql
(3 hunks)server/internal/adapter/gql/generated.go
(61 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(5 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 hunks)server/internal/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(4 hunks)server/internal/usecase/interactor/storytelling.go
(3 hunks)server/internal/usecase/interactor/storytelling_test.go
(1 hunks)server/internal/usecase/interfaces/story.go
(1 hunks)server/pkg/storytelling/story.go
(4 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (16)
- server/e2e/mock_test.go
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/internal/usecase/interfaces/story.go
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/internal/usecase/interactor/storytelling_test.go
- server/e2e/gql_project_test.go
- server/gql/storytelling.graphql
- server/internal/app/app.go
- server/e2e/ping_test.go
- server/e2e/gql_me_test.go
- server/internal/app/auth_client.go
- server/internal/usecase/interactor/storytelling.go
- server/pkg/storytelling/story.go
- server/pkg/storytelling/story_bulider.go
- server/internal/adapter/gql/gqlmodel/models_gen.go
⏰ 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 (16)
server/e2e/gql_storytelling_test.go (4)
49-52
: Google Analytics parameters correctly verified in story response.The test now properly validates the new GA parameters, ensuring that when
enableGa
is set to true andtrackingId
is set to "test-tracking-id" during story update, these values are correctly reflected in the API response.
254-256
: Google Analytics fields correctly added to the published story JSON.The test correctly verifies that the GA parameters are included in the published story JSON output, with default values when not explicitly set (
enableGa: false
andtrackingId: ""
).Also applies to: 367-368
513-514
: Google Analytics fields added to the GraphQL query.The query now fetches the new Google Analytics parameters (
enableGa
andtrackingId
) from the story, aligning with the PR objectives.
564-565
: UpdateStory mutation updated to include GA parameters.The mutation signature has been correctly updated to include the new Google Analytics parameters, satisfying the PR objective of adding GA support to the Story component.
Also applies to: 581-582
server/e2e/gql_asset_test.go (2)
122-179
: Comprehensive project-asset association test added.The new test function properly verifies:
- Creating assets with project associations
- Fetching assets by project ID
- Moving assets between projects
- Removing project associations
- Removing assets
This is a valuable addition that ensures the project-asset relationship functionality works correctly.
181-184
: CreateAssetMutation updated to support projectId parameter.The mutation has been correctly modified to include the optional projectId parameter, allowing assets to be associated with projects during creation.
server/e2e/common.go (4)
35-43
: Auth configuration refactored into a reusable variable.Good refactoring to extract the commonly used auth configuration settings into a reusable variable, which improves code maintainability and reduces duplication.
45-45
: Seeder function signature updated to include file gateway.The Seeder type now includes the file gateway parameter, which allows tests to properly initialize and use file-related functionality. This change is consistent across the codebase.
143-156
: Server initialization streamlined with better function names.The refactoring of server initialization functions improves code readability with more descriptive function names (
startServer
,ServerAndRepos
,Server
) and reduces code duplication by centralizing configuration.
284-296
: ValueDump function improved with better type handling.The function now properly handles different types of data using a type switch, making it more robust when debugging different response structures.
server/internal/adapter/gql/generated.go (6)
8100-8111
: Google Analytics fields added to Story type as requiredThis change adds the two new Google Analytics fields to the Story type:
enableGa: Boolean!
- Flag to enable/disable Google AnalyticstrackingId: String!
- The Google Analytics tracking IDThese changes align with the PR objectives to integrate Google Analytics parameters into the Story component.
8169-8175
: Google Analytics fields correctly added to UpdateStoryInputThe implementation properly adds the Google Analytics fields to the UpdateStoryInput:
enableGa: Boolean
- Optional field to update GA enablementtrackingId: String
- Optional field to update the GA tracking IDThese changes align with the PR objectives and follow the proper pattern of making fields optional in update inputs.
41920-42006
: Field resolvers implemented for new GA fieldsThe resolvers for
_Story_enableGa
and_Story_trackingId
have been properly implemented with appropriate error handling and null checking.The implementation appears to be complete and matches the schema definition.
52011-52025
: Input unmarshalling logic added for GA fieldsThe unmarshalling logic correctly handles the new
enableGa
andtrackingId
fields in theUpdateStoryInput
, with appropriate error handling and path context.This implementation will properly parse incoming GraphQL requests that include these fields.
6951-7001
: Asset-Project relationship enhancementsIn addition to the GA support, this PR adds Project relationship to Assets:
- Adds
projectId
field to Asset type- Creates new UpdateAssetInput and UpdateAssetPayload types
- Enables associating assets with specific projects
While not explicitly mentioned in the PR objectives, these changes provide improved asset management capabilities.
7021-7035
: GraphQL operations extended for asset managementThe implementation properly extends:
- The Query type with projectId filtering for assets
- The Mutation type with updateAsset operation
These changes enable more targeted asset queries and management capabilities, which complement the Story improvements.
cf9580b
to
fb9a4ff
Compare
fix permmision error associate project asset remove comment fix test error Undo comment out Passing GA parameters
fb9a4ff
to
d6707d8
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.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 2
♻️ Duplicate comments (5)
server/internal/infrastructure/mongo/asset.go (1)
96-100
: 🛠️ Refactor suggestionHandle type assertion failure case.
The type assertion to
bson.M
could silently fail if the assertion is unsuccessful. Consider handling the failure case explicitly.Apply this diff to handle the failure case:
- if andFilter, ok := mongox.And(filter, "url", bson.M{ - "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, - }).(bson.M); ok { - filter = andFilter - } + if andResult := mongox.And(filter, "url", bson.M{ + "$regex": primitive.Regex{Pattern: bucketPattern, Options: "i"}, + }); andResult != nil { + if andFilter, ok := andResult.(bson.M); ok { + filter = andFilter + } + }server/e2e/gql_project_export_import_test.go (1)
122-141
:⚠️ Potential issueAdd error handling for HTTP response in
exporProject
.The function should check the HTTP response status and handle potential errors before writing to file.
Apply this diff to improve error handling:
downloadResponse := e.GET(fmt.Sprintf("http://localhost:8080%s", downloadPath)). Expect(). Status(http.StatusOK). Body().Raw() +if len(downloadResponse) == 0 { + t.Fatal("Empty response from export endpoint") +} fileName := "project_data.zip" err := os.WriteFile(fileName, []byte(downloadResponse), os.ModePerm) assert.Nil(t, err)server/internal/usecase/interactor/asset.go (2)
108-127
:⚠️ Potential issueValidate workspace-to-project consistency.
When updating an existing asset's project, the code does not explicitly confirm the new project is in the same workspace or that the operator can write to it. This might allow transferring an asset to a project in a workspace where the user lacks permission.
Apply this fix to validate the target project:
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 } + // Verify the target project is valid and operator has permissions + if pid != nil { + proj, err := i.repos.Project.FindByID(ctx, *pid) + if err != nil { + return aid, pid, err + } + if proj == nil { + return aid, pid, interfaces.ErrNotFound + } + // Check if project is in the same workspace as the asset + if proj.Workspace() != asset.Workspace() { + return aid, pid, interfaces.ErrOperationDenied + } + // Check if operator has write permissions to the project's workspace + if !operator.IsWritableWorkspace(proj.Workspace()) { + return aid, pid, interfaces.ErrOperationDenied + } + } + asset.SetProject(pid) return aid, pid, i.repos.Asset.Save(ctx, asset) }, ) }
166-166
:⚠️ Potential issueUse actual file contents for content type detection.
Currently,
http.DetectContentType([]byte(zipFile.Name))
checks the filename rather than the file contents, leading to inaccurate content type detection.Apply this fix to read the first 512 bytes for proper content type detection:
-contentType := http.DetectContentType([]byte(zipFile.Name)) +buf := make([]byte, 512) +n, err := readCloser.Read(buf) +if err != nil && err != io.EOF { + return nil, 0, fmt.Errorf("error reading file header: %w", err) +} +// Need to re-open the file since we've now read part of it +if cerr := readCloser.Close(); cerr != nil { + return nil, 0, fmt.Errorf("error closing file after header read: %w", cerr) +} +readCloser, err = zipFile.Open() +if err != nil { + return nil, 0, fmt.Errorf("error reopening file after content detection: %w", err) +} +contentType := http.DetectContentType(buf[:n])Don't forget to add the import for
io
package:import ( ... + "io" ... )
server/e2e/seeder.go (1)
203-223
:⚠️ Potential issueCritical error:
if err != err
condition never succeeds.These lines compare
err
to itself, causing the error handling block to never run. This effectively bypasses error handling. Replace withif err != nil
to correctly handle potential errors.- if err = r.Scene.Save(ctx, s); err != err { + if err = r.Scene.Save(ctx, s); err != nil { return err } - if err = r.Property.Save(ctx, prop); err != err { + if err = r.Property.Save(ctx, prop); err != nil { return err } - if err = r.Layer.Save(ctx, rootLayer); err != err { + if err = r.Layer.Save(ctx, rootLayer); err != nil { return err } - if err = addWidget(ctx, s, r); err != err { + if err = addWidget(ctx, s, r); err != nil { return err } - if err = addStory(ctx, r); err != err { + if err = addStory(ctx, r); err != nil { return err } - if err = addLayerSimple(ctx, r); err != err { + if err = addLayerSimple(ctx, r); err != nil { return err } - if err = addLayerStyle(s.ID(), ctx, r); err != err { + if err = addLayerStyle(s.ID(), ctx, r); err != nil { return err }
🧹 Nitpick comments (6)
server/internal/adapter/gql/resolver_mutation_asset.go (1)
41-62
: New UpdateAsset method with project association.The method allows updating an asset's project association, but there's an unused variable.
On line 48-49, you define
project := input.ProjectID
but then directly useinput.ProjectID
on line 49. Consider using the local variable:- if project := input.ProjectID; project != nil { - pidValue, err := gqlmodel.ToID[id.Project](*input.ProjectID) + if project := input.ProjectID; project != nil { + pidValue, err := gqlmodel.ToID[id.Project](*project)server/internal/usecase/interactor/project.go (1)
513-533
: Consider consolidating image export checks.The new
ExportProjectData
function includes logic for handling the project's image asset. As a future improvement, consider consolidating potential asset export checks (e.g., verifying if an image URL is valid) to prevent partial exports if the image asset is unresolvable.server/internal/adapter/gql/gqlmodel/models_gen.go (1)
138-138
: Add clarity on optional ProjectID in Asset.The new
ProjectID
field is optional, but its usage might be critical in certain workflows. Consider elaborating in documentation or resolvers on whether this field is mandatory in practice and how it’s handled if omitted.server/e2e/gql_storytelling_test.go (2)
15-55
: Comprehensive Story CRUD coverage.This test thoroughly exercises create, fetch, update, and delete story flows, including the new GA fields (
enableGa
/trackingId
). Good coverage. Consider a negative test to verify behavior when invalid GA parameters are provided.
57-152
: Robust Page CRUD verification.The
TestStoryPageCRUD
method tests page creation, duplication, and deletion with thorough checks. Tests appear comprehensive. Potential improvement: add coverage for zero or negative indices.server/e2e/gql_asset_test.go (1)
17-18
: Improve test name or add descriptive comment.The function name
TestCoreSupportGetAssets
vaguely hints at testing assets with core support, but the actual test logic also creates assets withcoreSupport = false
. Consider either renaming the test (e.g.,TestGetAssetsWithAndWithoutCoreSupport
) or clarifying via inline comments that both scenarios are tested.
🛑 Comments failed to post (2)
server/internal/usecase/interactor/nlslayer.go (1)
1154-1158: 💡 Verification agent
🧩 Analysis chain
Verify all references to the new feature creation pattern
This change from implicit to explicit ID generation is a good improvement, but ensure all other places in the codebase that may have used the old pattern (
NewFeatureWithNewId
) have been updated.
🏁 Script executed:
#!/bin/bash # Search for any remaining instances of NewFeatureWithNewId echo "Looking for any remaining usages of NewFeatureWithNewId pattern..." rg "NewFeatureWithNewId" --type go echo "Checking all feature creation patterns to verify consistency..." rg "NewFeature\\(" --type go -A 2Length of output: 8435
Action Required: Update Test Cases for Feature Creation Pattern
The review verifies that the production code now consistently uses the new explicit ID generation approach via
NewFeature
withNewFeatureID()
. However, the shell script output still shows an instance in the test file (server/pkg/nlslayer/feature_test.go
) with a function namedTestNewFeatureWithNewId
. Please update these tests to use the new pattern for consistency.server/e2e/common.go (1)
35-43: 🛠️ Refactor suggestion
Global variable usage could lead to shared state between tests.
fr
is declared as a package-level variable, which may cause conflicts or unexpected behavior if tests run in parallel. Consider localizing this variable within functions or test setups to avoid potential concurrency issues.-var ( - fr *gateway.File - disabledAuthConfig = &config.Config{...} -) +var disabledAuthConfig = &config.Config{...} // ... // Pass gateway.File around instead of storing in 'fr' at package level.📝 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 disabledAuthConfig = &config.Config{ Origins: []string{"https://example.com"}, AuthSrv: config.AuthSrvConfig{ Disabled: true, }, } // Pass gateway.File around instead of storing in 'fr' at package level.
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: 0
🧹 Nitpick comments (6)
server/e2e/gql_storytelling_test.go (6)
15-55
: Consider verifying final deletion in TestStoryCRUD.This test thoroughly validates creating, fetching, updating, and deleting a story. As a good-to-have improvement, consider performing a final fetch after the deletion to confirm that the story is truly removed from the scene.
57-152
: Split TestStoryPageCRUD into smaller tests for maintainability.Currently, this single test covers creation, duplication, updating, and deletion. Splitting these operations into separate tests (e.g.,
TestCreatePage
,TestDuplicatePage
, etc.) can enhance clarity and simplify debugging.
154-206
: Add negative scenario coverage for block operations.The test covers creating, moving, and removing blocks successfully. Consider adding negative tests for invalid block IDs or out-of-range index positions to ensure the code handles error paths gracefully.
208-229
: Ensure property schema coverage in TestStoryPageBlocksProperties.These property updates and fetches look thorough. Consider testing error cases, such as invalid property types or missing required fields, to strengthen validation coverage.
231-413
: Consider externalizing large JSON in TestStoryPublishing.Storing your extensive expected JSON in a separate fixture file can make this test more readable and easier to maintain, especially when the structure changes frequently.
869-904
: Ensure error handling for removeBlock.The updated function removes a block and checks it no longer exists. Consider testing removal of an already-removed or invalid block to confirm robust error handling and prevent unwanted side effects.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
server/e2e/common.go
(2 hunks)server/e2e/gql_asset_test.go
(2 hunks)server/e2e/gql_me_test.go
(1 hunks)server/e2e/gql_project_test.go
(2 hunks)server/e2e/gql_storytelling_test.go
(6 hunks)server/e2e/mock_test.go
(1 hunks)server/e2e/ping_test.go
(1 hunks)server/gql/storytelling.graphql
(3 hunks)server/internal/adapter/gql/generated.go
(20 hunks)server/internal/adapter/gql/gqlmodel/convert_storytelling.go
(1 hunks)server/internal/adapter/gql/gqlmodel/models_gen.go
(2 hunks)server/internal/adapter/gql/resolver_mutation_storytelling.go
(1 hunks)server/internal/app/app.go
(2 hunks)server/internal/app/auth_client.go
(1 hunks)server/internal/infrastructure/mongo/mongodoc/storytelling.go
(3 hunks)server/internal/usecase/interactor/storytelling.go
(3 hunks)server/internal/usecase/interactor/storytelling_test.go
(1 hunks)server/internal/usecase/interfaces/story.go
(1 hunks)server/pkg/storytelling/story.go
(3 hunks)server/pkg/storytelling/story_bulider.go
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (18)
- server/internal/usecase/interfaces/story.go
- server/internal/usecase/interactor/storytelling_test.go
- server/e2e/gql_asset_test.go
- server/e2e/mock_test.go
- server/internal/adapter/gql/gqlmodel/convert_storytelling.go
- server/internal/adapter/gql/resolver_mutation_storytelling.go
- server/internal/app/app.go
- server/internal/infrastructure/mongo/mongodoc/storytelling.go
- server/pkg/storytelling/story_bulider.go
- server/internal/app/auth_client.go
- server/gql/storytelling.graphql
- server/internal/usecase/interactor/storytelling.go
- server/e2e/ping_test.go
- server/pkg/storytelling/story.go
- server/e2e/gql_project_test.go
- server/internal/adapter/gql/gqlmodel/models_gen.go
- server/internal/adapter/gql/generated.go
- server/e2e/common.go
🔇 Additional comments (3)
server/e2e/gql_me_test.go (1)
8-8
: Ensure test environment consistency.Replacing
StartServer
withServer
removes explicit test configuration (e.g., authentication or origin settings). Verify that the test environment remains aligned with the original expectations, especially if your tests rely on disabled authentication or other config parameters.server/e2e/gql_storytelling_test.go (2)
501-514
: Validate new fields in fetchSceneForStories.The query now includes additional fields such as
enableGa
andtrackingId
. Ensure that all resolvers and front-end queries handle these fields properly, and consider cases where they might be absent or set to default values.
561-583
: Confirm default behavior for new GA fields in updateStory.Introducing
enableGa
andtrackingId
is beneficial, but confirm that your application handles default or null values without errors, especially if these fields are optional in GraphQL inputs.
Overview
Add Google Analytics parameters to the Story.
Notion:#1425
What I've done
I added the enableGa and trackingId parameters to the Story parameters, just like in the current project, and I also added the parameters to updateStory.
What I haven't done
How I tested
added enableGa and trackingId to the existing test code.
https://github.com/reearth/reearth-visualizer/pull/1425/files#diff-8f0c77ec34d3e5a7b00a586b79ff265e70a9ffc641a7d4eb568fc7c69be12616R15
Which point I want you to review particularly
Memo
Fix the front-end query here
web/src/services/gql/fragments/story.ts
enableGa and trackingId will be added to UpdateStoryInput.
Summary by CodeRabbit
New Features
Refactor
Tests