Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(server): sync sketch layer properties #1348

Merged
merged 13 commits into from
Jan 17, 2025

Conversation

hexaforce
Copy link
Contributor

@hexaforce hexaforce commented Jan 14, 2025

Overview

Synchronize data updates in the sketch layer.

What I've done

To synchronize the data, the server needs to know about title renames and deleted titles.
Therefore, the API has been updated.

Updated GraphQL:

  • Removed addCustomProperties.
  • Added changeCustomPropertyTitle and removeCustomProperty.

Note: Since addCustomProperties is identical to updateCustomProperties, please call updateCustomProperties instead.

-input AddCustomPropertySchemaInput {
-  layerId: ID!
-  schema: JSON
-}

 input UpdateCustomPropertySchemaInput {
   layerId: ID!
   schema: JSON
 }

+input ChangeCustomPropertyTitleInput {
+  layerId: ID!
+  schema: JSON
+  oldTitle: String!
+  newTitle: String!
+}

+input RemoveCustomPropertyInput {
+  layerId: ID!
+  schema: JSON
+  removedTitle: String!
+}

 extend type Mutation {
-  addCustomProperties(
-    input: AddCustomPropertySchemaInput!
-  ): UpdateNLSLayerPayload!
   updateCustomProperties(
     input: UpdateCustomPropertySchemaInput!
   ): UpdateNLSLayerPayload!
+  changeCustomPropertyTitle(
+    input: ChangeCustomPropertyTitleInput!
+  ): UpdateNLSLayerPayload!
+  removeCustomProperty(
+    input: RemoveCustomPropertyInput!
+  ): UpdateNLSLayerPayload!
 }

What I haven't done

The GeoJSON format check will be implemented in a separate pull request.

How I tested

I added a method to use assert.Regexp for checking with regular expressions in tests:
func JSONEqRegexp(t *testing.T, actual string, expected string) bool

Using this, I created the following end-to-end (e2e) tests:
https://github.com/reearth/reearth-visualizer/pull/1348/files#diff-b1a67b8e570b786682445adb6c015cc17d86ce6af88589b785e110089b289e1b

Which point I want you to review particularly

When changing the type of a custom property, the value remains unchanged.
Therefore, along with calling UpdateCustomProperties, you also need to update the value using UpdateGeoJSONFeature.

# CustomProperties
"AAA": "Text_1"
↓↓↓
"AAA": "Int_1"

# GeoJSONFeature
"updateGeoJSONFeature": {
    "properties": {
        "AAA": "aaa",
↓↓↓
"updateGeoJSONFeature": {
    "properties": {
        "AAA": 123,

Memo

The implementation details for the web have been documented in Notion.
https://www.notion.so/eukarya/BE-Support-synchronization-of-properties-after-schema-changes-of-sketch-layer-17716e0fb165806eb2d1eef2fb6244e3

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced custom property management for layers
      • Added ability to change custom property titles
      • Added functionality to remove custom properties from layers
  • Improvements

    • Streamlined JSON comparison and handling in testing utilities
    • Simplified test setup and request handling
    • Refined GraphQL schema for custom property operations
    • Introduced error handling for JSON serialization processes
  • Testing

    • Added comprehensive end-to-end tests for custom property management
    • Improved test utility functions for JSON processing and comparison
  • Chores

    • Downgraded Go version from 1.23.4 to 1.21 in the module configuration

@hexaforce hexaforce self-assigned this Jan 14, 2025
@hexaforce hexaforce requested a review from pyshx as a code owner January 14, 2025 01:08
Copy link

coderabbitai bot commented Jan 14, 2025

Walkthrough

This pull request introduces comprehensive changes to custom property management in the GraphQL layer. The modifications focus on enhancing the functionality for updating, changing titles, and removing custom properties across multiple server components. The changes span GraphQL schema definitions, resolver implementations, and internal layer interaction methods, providing a more flexible approach to managing custom properties in layers. Additionally, improvements to JSON handling functions and the introduction of end-to-end tests for custom properties are included.

Changes

File Change Summary
server/e2e/common.go Enhanced JSON comparison functions, added utility methods for JSON handling. Renamed parameters in JSONEqRegexp function and added new functions for JSON processing.
server/e2e/gql_custom_property_test.go Added end-to-end tests for custom property management, including updating, changing titles, and removing properties.
server/e2e/gql_project_import_test.go Removed toJSONString function impacting JSON serialization.
server/e2e/gql_storytelling_test.go Refactored HTTP request handling and removed redundant JSON comparison functions.
server/gql/newlayer.graphql Renamed and added input types, modified mutation definitions for custom properties, including the introduction of changeCustomPropertyTitle and removeCustomProperty mutations.
server/internal/adapter/gql/generated.go Updated mutation and complexity resolvers, removed AddCustomProperties function, added new methods for changing and removing properties.
server/internal/adapter/gql/gqlmodel/models_gen.go Added new input types for custom property operations, removed AddCustomPropertySchemaInput.
server/internal/adapter/gql/resolver_mutation_nlslayer.go Introduced new methods for changing and removing custom properties.
server/internal/usecase/interactor/nlslayer.go Added methods for changing and removing custom properties.
server/internal/usecase/interfaces/nlslayer.go Extended interface with new custom property management methods.
server/go.mod Downgraded Go version from 1.23.4 to 1.21, removed toolchain directive.
server/e2e/gql_nlslayer_test.go Renamed addCustomProperties function to updateCustomProperties to reflect updated functionality.

Possibly related PRs

Suggested Labels

web

Suggested Reviewers

  • mkumbobeaty
  • airslice
  • pyshx

Poem

🐰 Hop, hop, custom properties dance,
Titles change with a rabbit's glance,
Schema shifts like morning dew,
GraphQL magic, sparkling and new,
Layer's secrets, now set free! 🌈

Finishing Touches

  • 📝 Generate Docstrings (Beta)

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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Jan 14, 2025

Deploy Preview for reearth-web ready!

Name Link
🔨 Latest commit 661dbb9
🔍 Latest deploy log https://app.netlify.com/sites/reearth-web/deploys/6789f726ab3c9d00089f0562
😎 Deploy Preview https://deploy-preview-1348--reearth-web.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (3)
server/e2e/gql_storytelling_test.go (1)

Line range hint 985-994: Improve test maintainability.

The JSON comparison could be simplified by:

  1. Moving the expected JSON to a test fixture file
  2. Using constants for repeated values like plugin IDs
  3. Breaking down the large JSON into smaller, focused test cases

Example refactor:

+const (
+    testPluginID = "reearth"
+    testExtensionID = "textStoryBlock"
+)
+
+func loadTestFixture(t *testing.T, name string) string {
+    data, err := os.ReadFile(filepath.Join("testdata", name))
+    assert.NoError(t, err)
+    return string(data)
+}
+
 func TestStoryPublishing(t *testing.T) {
     // ... test setup ...
-    expected := fmt.Sprintf(`{...}`, sID, storyID, extensionId, blockID, pageID, pluginId)
+    expected := fmt.Sprintf(loadTestFixture(t, "story_publishing.json"),
+        sID, storyID, testExtensionID, blockID, pageID, testPluginID)
     RegexpJSONEReadCloser(t, rc, expected)
     // ... rest of test ...
 }
server/e2e/gql_custom_property_test.go (2)

11-99: Consider adding more test cases.

While the happy path is well tested, consider adding tests for:

  • Edge cases (empty properties, non-existent titles)
  • Error cases (invalid inputs, transaction failures)

266-279: Consider enhancing test data setup.

The setupTestData function could be more robust by:

  • Adding validation for created resources
  • Returning error instead of using multiple return values
  • Adding cleanup in case of partial setup failure
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52f66e4 and 1fdaea6.

📒 Files selected for processing (10)
  • server/e2e/common.go (2 hunks)
  • server/e2e/gql_custom_property_test.go (1 hunks)
  • server/e2e/gql_project_import_test.go (0 hunks)
  • server/e2e/gql_storytelling_test.go (25 hunks)
  • server/gql/newlayer.graphql (2 hunks)
  • server/internal/adapter/gql/generated.go (23 hunks)
  • server/internal/adapter/gql/gqlmodel/models_gen.go (2 hunks)
  • server/internal/adapter/gql/resolver_mutation_nlslayer.go (2 hunks)
  • server/internal/usecase/interactor/nlslayer.go (2 hunks)
  • server/internal/usecase/interfaces/nlslayer.go (1 hunks)
💤 Files with no reviewable changes (1)
  • server/e2e/gql_project_import_test.go
🧰 Additional context used
🪛 GitHub Check: ci-server / ci-server-lint
server/internal/usecase/interactor/nlslayer.go

[failure] 752-752:
File is not properly formatted (gofmt)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: deploy-server / Deploy app to test env
🔇 Additional comments (18)
server/e2e/gql_storytelling_test.go (1)

708-709: LGTM! Good error case coverage.

Testing with an invalid page ID is a good practice for ensuring proper error handling.

server/internal/adapter/gql/generated.go (4)

1580-1581: LGTM! Consistent interface design.

The new mutation resolvers follow the established pattern and maintain consistency by returning UpdateNLSLayerPayload.


Line range hint 31541-31711: LGTM! Generated code follows gqlgen patterns.

The field context and argument handling code is properly generated and includes appropriate error handling.


Line range hint 60168-61984: Verify field order matches GraphQL schema.

The fieldsInOrder arrays define a specific processing order for input fields. Ensure this matches the order in the GraphQL schema to maintain consistency.

Run this script to compare field order:

✅ Verification successful

Field order verification completed successfully

The field order in the generated code matches exactly with the GraphQL schema definitions in server/gql/newlayer.graphql for both input types:

  • ChangeCustomPropertyTitleInput: layerId, schema, oldTitle, newTitle
  • RemoveCustomPropertyInput: layerId, schema, removedTitle
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Extract field order from GraphQL schema
echo "Schema field order:"
rg -A 5 'input (Change|Remove)CustomProperty.*Input \{' | rg '^\s+\w+:'

# Compare with fieldsInOrder in generated code
echo "Generated code field order:"
rg 'fieldsInOrder.*layerId.*schema.*(oldTitle|removedTitle)'

Length of output: 474


Script:

#!/bin/bash
# Search for input type definitions using ast-grep
echo "GraphQL Schema Input Types:"
ast-grep --pattern 'input ChangeCustomPropertyTitleInput {
  $$$
}'

echo -e "\nInput Type 2:"
ast-grep --pattern 'input RemoveCustomPropertyInput {
  $$$
}'

# Show more context from generated code
echo -e "\nGenerated Code Context:"
rg -B2 -A2 'fieldsInOrder.*layerId.*schema.*(oldTitle|removedTitle)'

Length of output: 1049


Script:

#!/bin/bash
# Find GraphQL schema files
echo "Looking for GraphQL schema files:"
fd -e graphql -e gql

# Search for input types in all files
echo -e "\nSearching for input type definitions:"
rg -A 6 'input (ChangeCustomPropertyTitleInput|RemoveCustomPropertyInput)' --type graphql

Length of output: 772


Script:

#!/bin/bash
# Search for input types in relevant schema files
echo "Searching in property.graphql:"
cat server/gql/property.graphql | grep -A 6 'input.*CustomProperty.*Input'

echo -e "\nSearching in layer.graphql:"
cat server/gql/layer.graphql | grep -A 6 'input.*CustomProperty.*Input'

echo -e "\nSearching in _shared.graphql:"
cat server/gql/_shared.graphql | grep -A 6 'input.*CustomProperty.*Input'

Length of output: 443


Script:

#!/bin/bash
# Broad search across all schema files
echo "Searching all schema files for input types:"
rg -i -A 10 'input.*custompropertyinput' server/gql/

# Alternative search pattern
echo -e "\nAlternative search:"
rg -i -A 10 'input.*\{.*title.*\}' server/gql/

Length of output: 880


Script:

#!/bin/bash
echo "Searching for ChangeCustomPropertyTitleInput:"
rg -i -A 10 'input.*ChangecustompropertytitleInput' server/gql/

# Alternative search pattern
echo -e "\nAlternative search:"
rg -i -A 10 'input.*\{.*oldTitle.*newTitle' server/gql/

Length of output: 1121


9450-9465: Consider adding schema validation for the JSON field.

The schema field of type JSON in all input types could benefit from runtime validation to prevent invalid schemas from causing issues.

Run this script to check if schema validation is implemented elsewhere:

server/internal/usecase/interfaces/nlslayer.go (1)

88-89: LGTM! Well-structured interface additions.

The new methods ChangeCustomPropertyTitle and RemoveCustomProperty are well-defined with clear parameters and consistent error handling patterns.

server/internal/adapter/gql/resolver_mutation_nlslayer.go (3)

Line range hint 221-239: LGTM! Clean implementation of UpdateCustomProperties.

The resolver correctly maps the GraphQL input to the usecase layer and handles errors appropriately.


240-257: LGTM! Well-structured ChangeCustomPropertyTitle implementation.

The resolver properly handles the title change operation with appropriate error handling.


Line range hint 259-275: LGTM! Clean implementation of RemoveCustomProperty.

The resolver follows the established pattern and properly handles the removal operation.

server/internal/usecase/interactor/nlslayer.go (2)

261-266: LGTM! Proper config update.

The code correctly updates the name in both the layer and its config properties.


686-730: LGTM! Well-implemented title change functionality.

The implementation properly handles:

  • Transaction management
  • Feature property updates
  • Schema updates
  • Error cases
server/e2e/gql_custom_property_test.go (1)

101-181: LGTM! Comprehensive test coverage for new mutations.

The tests thoroughly verify the behavior of ChangeCustomPropertyTitle and RemoveCustomProperty mutations.

Also applies to: 183-262

server/gql/newlayer.graphql (4)

131-134: LGTM! Appropriate rename from AddCustomPropertySchemaInput

The rename better reflects the operation's purpose since it's updating the schema rather than just adding properties.


136-141: LGTM! Well-structured input type for title changes

The input type properly captures all necessary fields for renaming a custom property, with appropriate nullability.


143-146: LGTM! Well-structured input type for property removal

The input type correctly captures all necessary fields for removing a custom property, with appropriate nullability.


213-218: LGTM! Consistent mutation definitions

The new mutations follow the established pattern of layer operations, using appropriate input types and return values.

server/internal/adapter/gql/gqlmodel/models_gen.go (2)

273-278: LGTM! Correctly generated struct

The generated struct matches the GraphQL schema definition with appropriate Go types and JSON tags.


1393-1397: LGTM! Correctly generated struct

The generated struct matches the GraphQL schema definition with appropriate Go types and JSON tags.

@hexaforce hexaforce changed the title add sync api feat(server): sync sketch layer properties Jan 14, 2025
Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
server/internal/usecase/interactor/nlslayer.go (1)

261-266: Add error handling for type assertions.

The type assertions for accessing the config properties could fail silently. Consider adding explicit type checks to handle potential errors.

 if config := layer.Config(); config != nil {
     c := *config
-    if properties, ok := c["properties"].(map[string]interface{}); ok {
-        properties["name"] = *inp.Name
+    if props, ok := c["properties"]; ok {
+        if properties, ok := props.(map[string]interface{}); ok {
+            properties["name"] = *inp.Name
+            layer.UpdateConfig(&c)
+        }
     }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 09116d3 and b31edf7.

📒 Files selected for processing (2)
  • server/go.mod (0 hunks)
  • server/internal/usecase/interactor/nlslayer.go (2 hunks)
💤 Files with no reviewable changes (1)
  • server/go.mod
🔇 Additional comments (2)
server/internal/usecase/interactor/nlslayer.go (2)

752-752: Fix the formatting issue in the for loop.

The code has a formatting issue in the range clause.

-			for k := range *props {
+			for k := range *props {

686-774: Verify the impact on direct callers.

Let's check for any direct callers of these methods that might need updates:

✅ Verification successful

Implementation and integration verified successfully

The methods are properly integrated into the codebase with complete GraphQL resolver implementation and E2E test coverage. All callers are correctly using the new methods.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find direct callers of the new methods

# Search for direct calls to ChangeCustomPropertyTitle
echo "Searching for ChangeCustomPropertyTitle callers..."
rg "ChangeCustomPropertyTitle\(" -A 3

# Search for direct calls to RemoveCustomProperty
echo "Searching for RemoveCustomProperty callers..."
rg "RemoveCustomProperty\(" -A 3

Length of output: 8292

Copy link

@coderabbitai coderabbitai bot left a 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 (2)
server/e2e/gql_nlslayer_test.go (2)

Line range hint 737-762: Add response validation in the helper function.

The function should validate the GraphQL response to ensure the mutation was successful. This helps catch issues early in the tests.

 func updateCustomProperties(
 	e *httpexpect.Expect,
 	layerId string,
 	schema map[string]any,
 ) (GraphQLRequest, *httpexpect.Value) {
 	requestBody := GraphQLRequest{
 		OperationName: "UpdateCustomProperties",
 		Query: `mutation UpdateCustomProperties($input: UpdateCustomPropertySchemaInput!) {
 			updateCustomProperties(input: $input) {
 				layer {
 					id
 					sketch {
 						customPropertySchema
 					}
 				}
 			}
 		}`,
 		Variables: map[string]interface{}{
 			"input": map[string]interface{}{
 				"layerId": layerId,
 				"schema":  schema,
 			},
 		},
 	}

 	res := Request(e, uID.String(), requestBody)
+	// Validate response
+	res.Object().
+		Path("$.data.updateCustomProperties.layer.id").Equal(layerId)
+	res.Object().
+		Path("$.data.updateCustomProperties.layer.sketch.customPropertySchema").NotNull()

 	return requestBody, res
 }

810-810: Enhance test coverage with edge cases.

The test only covers successful updates. Consider adding test cases for:

  • Invalid schema formats
  • Schema type changes
  • Empty or null schema values
  • Concurrent updates to the same layer

Also applies to: 833-833

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b31edf7 and c489408.

📒 Files selected for processing (1)
  • server/e2e/gql_nlslayer_test.go (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: deploy-server / Deploy app to test env
🔇 Additional comments (1)
server/e2e/gql_nlslayer_test.go (1)

Line range hint 737-847: Add tests for new custom property mutations.

Based on the PR objectives, tests are missing for:

  • changeCustomPropertyTitle mutation
  • removeCustomProperty mutation

These new operations should be tested to ensure complete coverage of the custom property synchronization feature.

Copy link

@coderabbitai coderabbitai bot left a 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 (1)
server/internal/usecase/interactor/nlslayer.go (1)

753-813: ⚠️ Potential issue

Add scene write permission check.

The method should verify that the operator has write permissions for the scene before proceeding with the changes.

Add the permission check after retrieving the layer:

 layer, err := i.nlslayerRepo.FindByID(ctx, inp.LayerID)
 if err != nil {
     return nil, err
 }
+if err := i.CanWriteScene(layer.Scene(), operator); err != nil {
+    return nil, interfaces.ErrOperationDenied
+}
 if layer.Sketch() == nil || layer.Sketch().FeatureCollection() == nil {
🧹 Nitpick comments (2)
server/internal/usecase/interactor/nlslayer.go (2)

262-267: Add nil check for properties map.

The code should check if the "properties" field exists and is not nil before attempting to update it.

 if config := layer.Config(); config != nil {
     c := *config
     if properties, ok := c["properties"].(map[string]interface{}); ok {
+        if properties != nil {
             properties["name"] = *inp.Name
+        }
     }
 }

789-797: Optimize property removal loop.

The inner loop can be broken after finding and removing the property since we only need to remove one occurrence.

 for _, feature := range layer.Sketch().FeatureCollection().Features() {
     if props := feature.Properties(); props != nil {
         for k := range *props {
             if k == removedTitle {
                 delete(*props, k)
+                break
             }
         }
     }
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c489408 and f252aae.

📒 Files selected for processing (4)
  • server/e2e/common.go (2 hunks)
  • server/e2e/gql_asset_test.go (1 hunks)
  • server/e2e/gql_project_import_test.go (2 hunks)
  • server/internal/usecase/interactor/nlslayer.go (3 hunks)
🧰 Additional context used
📓 Learnings (1)
server/e2e/common.go (1)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1348
File: server/e2e/common.go:280-282
Timestamp: 2025-01-14T04:44:52.731Z
Learning: In test utilities that handle JSON marshaling/unmarshaling, always include proper error handling to help debug test failures. This applies to functions like `toJSONString` in the e2e test package.
🪛 GitHub Check: ci-server / ci-server-lint
server/e2e/common.go

[failure] 259-259:
Error return value of actual.Close is not checked (errcheck)

🪛 golangci-lint (1.62.2)
server/e2e/common.go

259-259: Error return value of actual.Close is not checked

(errcheck)

🪛 GitHub Actions: ci
server/e2e/common.go

[error] 259-259: Error return value of actual.Close is not checked (errcheck)

🔇 Additional comments (4)
server/e2e/common.go (4)

250-256: LGTM! Robust JSON comparison implementation.

The function correctly normalizes JSON strings before comparison and properly escapes regexp special characters.


266-270: LGTM! Clean implementation with proper error handling.

The function correctly marshals the interface and handles potential errors.


272-280: LGTM! Flexible JSON normalization.

The function correctly handles both JSON objects and arrays, with proper error handling.


282-287: LGTM! Well-implemented error handling.

The function properly wraps JSON marshaling errors with context.

Copy link

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (1)
server/e2e/common.go (1)

250-256: Enhance regex pattern escaping.

The function only escapes square brackets ([), but other regex special characters might need escaping too. Consider using regexp.QuoteMeta for comprehensive escaping.

-		regexp.MustCompile(strings.ReplaceAll(aligningJSON(t, expected), "[", "\\[")),
+		regexp.MustCompile(regexp.QuoteMeta(aligningJSON(t, expected))),
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f252aae and 1fefe3e.

📒 Files selected for processing (1)
  • server/e2e/common.go (2 hunks)
🧰 Additional context used
📓 Learnings (1)
server/e2e/common.go (1)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1348
File: server/e2e/common.go:280-282
Timestamp: 2025-01-14T04:44:52.731Z
Learning: In test utilities that handle JSON marshaling/unmarshaling, always include proper error handling to help debug test failures. This applies to functions like `toJSONString` in the e2e test package.
🔇 Additional comments (3)
server/e2e/common.go (3)

270-274: LGTM!

The function correctly handles JSON marshaling and comparison with proper error checking.


276-284: LGTM!

The function correctly normalizes JSON format and handles both objects and arrays with proper error checking.


286-291: LGTM!

The function implements proper error handling with good error context, aligning with the best practices for JSON utilities.

Copy link
Contributor

@pyshx pyshx left a comment

Choose a reason for hiding this comment

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

In server/internal/usecase/interactor/nlslayer.go AddOrUpdateCustomProperties method should make sure to sync update applied to already defined feature's property. for example cases like if type of an existing field has been changed...

Copy link

@coderabbitai coderabbitai bot left a 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/common.go (4)

250-256: LGTM! Consider adding documentation.

The implementation correctly handles JSON comparison with regexp support. Consider adding a docstring to explain the purpose and behavior of the function.

+// JSONEqRegexp compares two JSON strings for equality using regular expressions.
+// It normalizes both strings using aligningJSON before comparison.
 func JSONEqRegexp(t *testing.T, actual string, expected string) bool {

258-268: LGTM! Consider optimizing buffer allocation.

The implementation correctly handles resource cleanup and error handling. Consider pre-allocating the buffer with a reasonable size to optimize memory usage.

-	actualBuf := new(bytes.Buffer)
+	actualBuf := bytes.NewBuffer(make([]byte, 0, 1024)) // Pre-allocate 1KB

270-274: LGTM! Consider improving error messages.

The implementation correctly handles JSON comparison for interface types. Consider adding more descriptive error messages to help with debugging.

-	assert.Nil(t, err)
+	assert.Nil(t, err, "failed to marshal actual value to JSON")

276-284: LGTM! Consider improving error messages.

The implementation correctly handles JSON normalization. Consider adding more descriptive error messages to help with debugging.

 	var obj interface{}
 	err := json.Unmarshal([]byte(str), &obj)
-	assert.Nil(t, err)
+	assert.Nil(t, err, "failed to unmarshal JSON string: %s", str)
 	strBytes, err := json.Marshal(obj)
-	assert.Nil(t, err)
+	assert.Nil(t, err, "failed to marshal normalized JSON")
server/internal/usecase/interactor/nlslayer.go (2)

690-758: Consider extracting validation logic and standardizing error messages.

The implementation is solid with proper transaction handling, permission checks, and value preservation during title changes. Consider these improvements:

  1. Extract the title validation logic into a separate function as it's also used in RemoveCustomProperty.
  2. Use predefined error constants instead of fmt.Errorf for consistency with the codebase.
+func (i *NLSLayer) validateCustomPropertyTitle(features []nlslayer.Feature, oldTitle, newTitle string) error {
+    titleExists := false
+    for _, feature := range features {
+        if props := feature.Properties(); props != nil {
+            if _, ok := (*props)[oldTitle]; ok {
+                titleExists = true
+            }
+            if newTitle != "" {
+                if _, ok := (*props)[newTitle]; ok {
+                    return interfaces.ErrCustomPropertyTitleExists
+                }
+            }
+        }
+    }
+    if !titleExists {
+        return interfaces.ErrCustomPropertyNotFound
+    }
+    return nil
+}

760-820: Reuse validation logic from the suggested helper function.

The implementation is solid but has duplicate validation logic. Use the suggested validateCustomPropertyTitle function to reduce code duplication:

-    titleExists := false
-    for _, feature := range layer.Sketch().FeatureCollection().Features() {
-        if props := feature.Properties(); props != nil {
-            if _, ok := (*props)[removedTitle]; ok {
-                titleExists = true
-                break
-            }
-        }
-    }
-
-    if !titleExists {
-        return nil, fmt.Errorf("property with title %s not found", removedTitle)
-    }
+    if err := i.validateCustomPropertyTitle(layer.Sketch().FeatureCollection().Features(), removedTitle, ""); err != nil {
+        return nil, err
+    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fefe3e and cfa55e9.

📒 Files selected for processing (4)
  • server/e2e/common.go (2 hunks)
  • server/e2e/gql_asset_test.go (1 hunks)
  • server/e2e/gql_project_import_test.go (2 hunks)
  • server/internal/usecase/interactor/nlslayer.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • server/e2e/gql_asset_test.go
  • server/e2e/gql_project_import_test.go
🧰 Additional context used
📓 Learnings (1)
server/e2e/common.go (1)
Learnt from: hexaforce
PR: reearth/reearth-visualizer#1348
File: server/e2e/common.go:280-282
Timestamp: 2025-01-14T04:44:52.731Z
Learning: In test utilities that handle JSON marshaling/unmarshaling, always include proper error handling to help debug test failures. This applies to functions like `toJSONString` in the e2e test package.
🔇 Additional comments (3)
server/e2e/common.go (1)

286-291: LGTM! Error handling is well implemented.

The implementation correctly handles JSON conversion and error wrapping.

server/internal/usecase/interactor/nlslayer.go (2)

262-267: LGTM! Good synchronization of layer name with config.

The implementation correctly updates the "name" property in the config when the layer name changes, with proper nil checks and type assertions.


656-658: LGTM! Good security practice with early permission check.

The permission check is correctly placed before any modifications and uses the appropriate error constant.

@hexaforce hexaforce merged commit 110cb33 into main Jan 17, 2025
16 of 17 checks passed
@hexaforce hexaforce deleted the feat/sync-sketch-layer-properties branch January 17, 2025 06:28
hexaforce added a commit that referenced this pull request Jan 22, 2025
Co-authored-by: Tomokazu Tantaka <t.tantaka@eukarya.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants