diff --git a/server/e2e/gql_import_export_test.go b/server/e2e/gql_import_export_test.go index fd08c8bf1..1decfa26c 100644 --- a/server/e2e/gql_import_export_test.go +++ b/server/e2e/gql_import_export_test.go @@ -1,12 +1,18 @@ package e2e import ( + "encoding/json" + "fmt" "net/http" + "os" "testing" + "github.com/gavv/httpexpect/v2" "github.com/reearth/reearth/server/internal/app/config" + "github.com/stretchr/testify/assert" ) +// export REEARTH_DB=mongodb://localhost // go test -v -run TestCallExportProject ./e2e/... func TestCallExportProject(t *testing.T) { @@ -18,21 +24,116 @@ func TestCallExportProject(t *testing.T) { }, }, true, baseSeeder) - pID := createProject(e, "test") + pID := createProjectWithExternalImage(e, "test") _, _, sID := createScene(e, pID) - createStory(e, sID, "test", 0) + _, _, storyID := createStory(e, sID, "test", 0) + _, _, pageID := createPage(e, sID, storyID, "test", true) + + _, _, _ = createBlock(e, sID, storyID, pageID, "reearth", "imageStoryBlock", nil) + _, _, _ = createBlock(e, sID, storyID, pageID, "reearth", "imageStoryBlock", nil) + _, _, _ = createBlock(e, sID, storyID, pageID, "reearth", "imageStoryBlock", nil) + + _, res := fetchSceneForStories(e, sID) + + blocks := res.Object().Value("data").Object(). + Value("node").Object(). + Value("stories").Array().First().Object(). + Value("pages").Array().First().Object(). + Value("blocks").Array().Iter() + + propID1 := blocks[0].Object().Value("propertyId").Raw().(string) + propID2 := blocks[1].Object().Value("propertyId").Raw().(string) + propID3 := blocks[2].Object().Value("propertyId").Raw().(string) + + _, res = updatePropertyValue(e, propID1, "default", "", "src", "http://localhost:8080/assets/01jbbhhtq2jq7mx39dhyq1cfr2.png", "URL") + res.Path("$.data.updatePropertyValue.propertyField.value").Equal("http://localhost:8080/assets/01jbbhhtq2jq7mx39dhyq1cfr2.png") + + _, res = updatePropertyValue(e, propID2, "default", wID.String(), "src", "https://test.com/project.jpg", "URL") + res.Path("$.data.updatePropertyValue.propertyField.value").Equal("https://test.com/project.jpg") + + _, res = updatePropertyValue(e, propID3, "default", wID.String(), "src", "https://api.visualizer.test.reearth.dev/assets/01jbbhhtq2jq7mx39dhyq1cfr2.png", "URL") + res.Path("$.data.updatePropertyValue.propertyField.value").Equal("https://api.visualizer.test.reearth.dev/assets/01jbbhhtq2jq7mx39dhyq1cfr2.png") + + fileName := exporProject(t, e, pID) + + defer func() { + err := os.Remove(fileName) + assert.Nil(t, err) + }() + +} + +// export REEARTH_DB=mongodb://localhost +// go test -v -run TestCallImportProject ./e2e/... + +func TestCallImportProject(t *testing.T) { + + e := StartServer(t, &config.Config{ + Origins: []string{"https://example.com"}, + AuthSrv: config.AuthSrvConfig{ + Disabled: true, + }, + }, true, baseSeeder) + + filePath := "test.zip" + + r := importProject(t, e, filePath) + + r.Value("project").NotNull() + r.Value("plugins").Array() + r.Value("schema").Array() + r.Value("scene").NotNull() + r.Value("nlsLayer").Array() + r.Value("style").Array() + r.Value("story").NotNull() + + // fmt.Println(toJSONString(r.Raw())) +} + +func createProjectWithExternalImage(e *httpexpect.Expect, name string) string { + 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 + __typename + } + __typename + } + }`, + Variables: map[string]any{ + "name": name, + "description": "abc", + "imageUrl": "https://test.com/project.jpg", + "teamId": wID.String(), + "visualizer": "CESIUM", + "coreSupport": true, + }, + } + res := e.POST("/api/graphql"). + WithHeader("Origin", "https://example.com"). + WithHeader("X-Reearth-Debug-User", uID.String()). + WithHeader("Content-Type", "application/json"). + WithJSON(requestBody). + Expect(). + Status(http.StatusOK). + JSON() + return res.Path("$.data.createProject.project.id").Raw().(string) +} + +func exporProject(t *testing.T, e *httpexpect.Expect, p string) string { requestBody := GraphQLRequest{ OperationName: "ExportProject", Query: "mutation ExportProject($projectId: ID!) { exportProject(input: {projectId: $projectId}) { projectDataPath __typename } }", Variables: map[string]any{ - "projectId": pID, + "projectId": p, }, } - - e.POST("/api/graphql"). + r := e.POST("/api/graphql"). WithHeader("Origin", "https://example.com"). WithHeader("authorization", "Bearer test"). WithHeader("X-Reearth-Debug-User", uID.String()). @@ -41,14 +142,62 @@ func TestCallExportProject(t *testing.T) { Expect(). Status(http.StatusOK). JSON(). - Object(). + Object() + downloadPath := r. Value("data").Object(). Value("exportProject").Object(). Value("projectDataPath").String().Raw() + downloadResponse := e.GET(fmt.Sprintf("http://localhost:8080%s", downloadPath)). + Expect(). + Status(http.StatusOK). + Body().Raw() + fileName := "project_data.zip" + err := os.WriteFile(fileName, []byte(downloadResponse), os.ModePerm) + assert.Nil(t, err) + return fileName +} + +func importProject(t *testing.T, e *httpexpect.Expect, filePath string) *httpexpect.Object { + file, err := os.Open(filePath) + if err != nil { + t.Fatalf("failed to open file: %v", err) + } + defer func() { + if cerr := file.Close(); cerr != nil && err == nil { + err = cerr + } + }() + requestBody := map[string]interface{}{ + "operationName": "ImportProject", + "variables": map[string]interface{}{ + "teamId": wID.String(), + "file": nil, + }, + "query": `mutation ImportProject($teamId: ID!, $file: Upload!) { + importProject(input: {teamId: $teamId, file: $file}) { + projectData + __typename + } + }`, + } + r := e.POST("/api/graphql"). + WithHeader("Origin", "https://example.com"). + WithHeader("authorization", "Bearer test"). + WithHeader("X-Reearth-Debug-User", uID.String()). + WithMultipart(). + WithFormField("operations", toJSONString(requestBody)). + WithFormField("map", `{"0": ["variables.file"]}`). + WithFile("0", filePath). + Expect(). + Status(http.StatusOK). + JSON(). + Object() + projectData := r.Value("data").Object().Value("importProject").Object().Value("projectData") + projectData.NotNull() + return projectData.Object() +} - // downloadResponse := e.GET(fmt.Sprintf("http://localhost:8080%s", projectDataPath)). - // Expect(). - // Status(http.StatusOK). - // Body() - // fmt.Println(downloadResponse) +func toJSONString(v interface{}) string { + jsonData, _ := json.Marshal(v) + return string(jsonData) } diff --git a/server/e2e/test.zip b/server/e2e/test.zip new file mode 100644 index 000000000..f74690bb9 Binary files /dev/null and b/server/e2e/test.zip differ diff --git a/server/internal/adapter/context.go b/server/internal/adapter/context.go index 9a60e4d53..9436e31a1 100644 --- a/server/internal/adapter/context.go +++ b/server/internal/adapter/context.go @@ -14,11 +14,12 @@ import ( type ContextKey string const ( - contextUser ContextKey = "user" - contextOperator ContextKey = "operator" - ContextAuthInfo ContextKey = "authinfo" - contextUsecases ContextKey = "usecases" - contextMockAuth ContextKey = "mockauth" + contextUser ContextKey = "user" + contextOperator ContextKey = "operator" + ContextAuthInfo ContextKey = "authinfo" + contextUsecases ContextKey = "usecases" + contextMockAuth ContextKey = "mockauth" + contextCurrentHost ContextKey = "currenthost" ) var defaultLang = language.English @@ -49,6 +50,10 @@ func AttachMockAuth(ctx context.Context, mockAuth bool) context.Context { return context.WithValue(ctx, contextMockAuth, mockAuth) } +func AttachCurrentHost(ctx context.Context, currentHost string) context.Context { + return context.WithValue(ctx, contextCurrentHost, currentHost) +} + func User(ctx context.Context) *user.User { if v := ctx.Value(contextUser); v != nil { if u, ok := v.(*user.User); ok { @@ -122,3 +127,12 @@ func IsMockAuth(ctx context.Context) bool { } return false } + +func CurrentHost(ctx context.Context) string { + if v := ctx.Value(contextCurrentHost); v != nil { + if currentHost, ok := v.(string); ok { + return currentHost + } + } + return "" +} diff --git a/server/internal/app/auth_client.go b/server/internal/app/auth_client.go index 3db36248a..3f46a126f 100644 --- a/server/internal/app/auth_client.go +++ b/server/internal/app/auth_client.go @@ -113,6 +113,8 @@ func attachOpMiddleware(cfg *ServerConfig) echo.MiddlewareFunc { log.Debugfc(ctx, "auth: op: %#v", op) } + ctx = adapter.AttachCurrentHost(ctx, cfg.Config.Host) + c.SetRequest(req.WithContext(ctx)) return next(c) } diff --git a/server/internal/usecase/interactor/nlslayer.go b/server/internal/usecase/interactor/nlslayer.go index 79c894d15..80f7cefdf 100644 --- a/server/internal/usecase/interactor/nlslayer.go +++ b/server/internal/usecase/interactor/nlslayer.go @@ -3,7 +3,10 @@ package interactor import ( "context" "errors" + "net/url" + "strings" + "github.com/reearth/reearth/server/internal/adapter" "github.com/reearth/reearth/server/internal/usecase" "github.com/reearth/reearth/server/internal/usecase/interfaces" "github.com/reearth/reearth/server/internal/usecase/repo" @@ -17,6 +20,7 @@ import ( "github.com/reearth/reearth/server/pkg/scene/builder" "github.com/reearth/reearthx/account/accountusecase/accountrepo" "github.com/reearth/reearthx/idx" + "github.com/reearth/reearthx/log" "github.com/reearth/reearthx/rerror" "github.com/reearth/reearthx/usecasex" ) @@ -854,6 +858,32 @@ func (i *NLSLayer) ImportNLSLayers(ctx context.Context, sceneID idx.ID[id.Scene] nlayerIDs = append(nlayerIDs, newNLSLayerID) replaceNLSLayerIDs[nlsLayerJSON.ID] = newNLSLayerID + if nlsLayerJSON.Config != nil { + config := *nlsLayerJSON.Config + if data, ok := config["data"].(map[string]interface{}); ok { + if u, ok := data["url"].(string); ok { + urlVal, err := url.Parse(u) + if err != nil { + log.Infofc(ctx, "invalid url: %v\n", err.Error()) + return nil, nil, err + } + if urlVal.Host == "localhost:8080" || strings.HasSuffix(urlVal.Host, ".reearth.dev") || strings.HasSuffix(urlVal.Host, ".reearth.io") { + currentHost := adapter.CurrentHost(ctx) + currentHost = strings.TrimPrefix(currentHost, "https://") + currentHost = strings.TrimPrefix(currentHost, "http://") + urlVal.Host = currentHost + if currentHost == "localhost:8080" { + urlVal.Scheme = "http" + } else { + urlVal.Scheme = "https" + } + data["url"] = urlVal.String() + } + } + } + + } + nlBuilder := nlslayer.New(). ID(newNLSLayerID). Simple(). @@ -871,7 +901,7 @@ func (i *NLSLayer) ImportNLSLayers(ctx context.Context, sceneID idx.ID[id.Scene] if err != nil { return nil, nil, err } - prop, err = builder.AddItemFromPropertyJSON(prop, schema, nlsLayerJSON.Infobox.Property) + prop, err = builder.AddItemFromPropertyJSON(ctx, prop, schema, nlsLayerJSON.Infobox.Property) if err != nil { return nil, nil, err } @@ -888,7 +918,7 @@ func (i *NLSLayer) ImportNLSLayers(ctx context.Context, sceneID idx.ID[id.Scene] if err != nil { return nil, nil, err } - propB, err = builder.AddItemFromPropertyJSON(propB, schemaB, b.Property) + propB, err = builder.AddItemFromPropertyJSON(ctx, propB, schemaB, b.Property) if err != nil { return nil, nil, err } diff --git a/server/internal/usecase/interactor/nlslayer_test.go b/server/internal/usecase/interactor/nlslayer_test.go index 629945f33..5372f28c8 100644 --- a/server/internal/usecase/interactor/nlslayer_test.go +++ b/server/internal/usecase/interactor/nlslayer_test.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + "github.com/reearth/reearth/server/internal/adapter" "github.com/reearth/reearth/server/internal/adapter/gql/gqlmodel" "github.com/reearth/reearth/server/internal/infrastructure/memory" "github.com/reearth/reearth/server/internal/usecase" @@ -264,6 +265,7 @@ func TestDeleteGeoJSONFeature(t *testing.T) { // go test -v -run TestImportNLSLayers ./internal/usecase/interactor/... func TestImportNLSLayers(t *testing.T) { ctx := context.Background() + ctx = adapter.AttachCurrentHost(ctx, "https://xxxx.reearth.dev") db := memory.New() ifl := NewNLSLayer(db) @@ -323,7 +325,7 @@ func TestImportNLSLayers(t *testing.T) { "lngColumn": "lng" }, "type": "csv", - "url": "http://localhost:8080/assets/01j7g9gpba44e0nxwc727nax0q.csv" + "url": "https://xxxx.reearth.dev/assets/01j7g9gpba44e0nxwc727nax0q.csv" } }, "title": "japan_architecture (2).csv", diff --git a/server/internal/usecase/interactor/plugin.go b/server/internal/usecase/interactor/plugin.go index df315405c..bf6eee16c 100644 --- a/server/internal/usecase/interactor/plugin.go +++ b/server/internal/usecase/interactor/plugin.go @@ -95,8 +95,7 @@ func (i *Plugin) ExportPlugins(ctx context.Context, sce *scene.Scene, zipWriter return nil, nil, err } - _, err = io.Copy(zipEntry, stream) - if err != nil { + if _, err = io.Copy(zipEntry, stream); err != nil { _ = stream.Close() return nil, nil, err } diff --git a/server/internal/usecase/interactor/project.go b/server/internal/usecase/interactor/project.go index 0c0e3ee47..dad27e075 100644 --- a/server/internal/usecase/interactor/project.go +++ b/server/internal/usecase/interactor/project.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/reearth/reearth/server/internal/adapter" "github.com/reearth/reearth/server/internal/adapter/gql/gqlmodel" jsonmodel "github.com/reearth/reearth/server/internal/adapter/gql/gqlmodel" "github.com/reearth/reearth/server/internal/usecase" @@ -616,6 +617,17 @@ func (i *Project) ImportProject(ctx context.Context, teamID string, projectData } if p.ImageURL != nil { + if p.ImageURL.Host == "localhost:8080" || strings.HasSuffix(p.ImageURL.Host, ".reearth.dev") || strings.HasSuffix(p.ImageURL.Host, ".reearth.io") { + currentHost := adapter.CurrentHost(ctx) + currentHost = strings.TrimPrefix(currentHost, "https://") + currentHost = strings.TrimPrefix(currentHost, "http://") + if currentHost == "localhost:8080" { + p.ImageURL.Scheme = "http" + } else { + p.ImageURL.Scheme = "https" + } + p.ImageURL.Host = currentHost + } prjBuilder = prjBuilder.ImageURL(p.ImageURL) } diff --git a/server/internal/usecase/interactor/project_test.go b/server/internal/usecase/interactor/project_test.go index 7579f07b0..d9283e628 100644 --- a/server/internal/usecase/interactor/project_test.go +++ b/server/internal/usecase/interactor/project_test.go @@ -7,6 +7,7 @@ import ( "net/url" "testing" + "github.com/reearth/reearth/server/internal/adapter" "github.com/reearth/reearth/server/internal/adapter/gql/gqlmodel" "github.com/reearth/reearth/server/internal/infrastructure/fs" "github.com/reearth/reearth/server/internal/infrastructure/memory" @@ -149,6 +150,7 @@ func TestProject_Create(t *testing.T) { func TestImportProject(t *testing.T) { ctx := context.Background() + ctx = adapter.AttachCurrentHost(ctx, "https://xxxx.reearth.dev") db := memory.New() ifp := NewProject(db, &gateway.Container{ @@ -232,10 +234,10 @@ func TestImportProject(t *testing.T) { "publicImage": "", "publicNoIndex": false, "imageUrl": { - "Scheme": "http", + "Scheme": "https", "Opaque": "", "User": null, - "Host": "localhost:8080", + "Host": "xxxx.reearth.dev", "Path": "/assets/01j7g9d988ct8hajjxfsb6e1n6.jpeg", "RawPath": "", "OmitHost": false, diff --git a/server/internal/usecase/interactor/scene.go b/server/internal/usecase/interactor/scene.go index 31fe3f60d..8ebbf8c54 100644 --- a/server/internal/usecase/interactor/scene.go +++ b/server/internal/usecase/interactor/scene.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/reearth/reearth/server/internal/adapter" "github.com/reearth/reearth/server/internal/usecase" "github.com/reearth/reearth/server/internal/usecase/gateway" "github.com/reearth/reearth/server/internal/usecase/interfaces" @@ -24,6 +25,7 @@ import ( "github.com/reearth/reearth/server/pkg/scene/builder" "github.com/reearth/reearth/server/pkg/visualizer" "github.com/reearth/reearthx/idx" + "github.com/reearth/reearthx/log" "github.com/reearth/reearthx/rerror" "github.com/reearth/reearthx/usecasex" "github.com/samber/lo" @@ -641,12 +643,13 @@ func (i *Scene) ExportScene(ctx context.Context, prj *project.Project, zipWriter c := actualLayer.Config() if c != nil { actualConfig := *c - data, ok := actualConfig["data"].(map[string]any) - if ok { - url, ok := data["url"].(string) + if data, ok := actualConfig["data"].(map[string]any); ok { + u, ok := data["url"].(*url.URL) if ok { - if err := i.addZipAsset(ctx, zipWriter, url); err != nil { - return nil, nil, errors.New("Fail addZipAsset :" + err.Error()) + if isReearth, u := convertReearthEnv(ctx, u); isReearth { + if err := i.addZipAsset(ctx, zipWriter, u.Path); err != nil { + log.Infofc(ctx, "Fail nLayer addZipAsset :", err.Error()) + } } } } @@ -677,8 +680,10 @@ func (i *Scene) ExportScene(ctx context.Context, prj *project.Project, zipWriter if !ok { continue } - if err := i.addZipAsset(ctx, zipWriter, u.Path); err != nil { - return nil, nil, errors.New("Fail addZipAsset :" + err.Error()) + if isReearth, u := convertReearthEnv(ctx, u); isReearth { + if err := i.addZipAsset(ctx, zipWriter, u.Path); err != nil { + log.Infofc(ctx, "Fail widget addZipAsset :", err.Error()) + } } } } @@ -704,8 +709,10 @@ func (i *Scene) ExportScene(ctx context.Context, prj *project.Project, zipWriter if !ok { continue } - if err := i.addZipAsset(ctx, zipWriter, u.Path); err != nil { - return nil, nil, errors.New("Fail addZipAsset :" + err.Error()) + if isReearth, u := convertReearthEnv(ctx, u); isReearth { + if err := i.addZipAsset(ctx, zipWriter, u.Path); err != nil { + log.Infofc(ctx, "Fail page block addZipAsset :", err.Error()) + } } } } @@ -718,6 +725,22 @@ func (i *Scene) ExportScene(ctx context.Context, prj *project.Project, zipWriter return sce, res, nil } +func convertReearthEnv(ctx context.Context, u *url.URL) (bool, *url.URL) { + if u.Host == "localhost:8080" || strings.HasSuffix(u.Host, ".reearth.dev") || strings.HasSuffix(u.Host, ".reearth.io") { + currentHost := adapter.CurrentHost(ctx) + currentHost = strings.TrimPrefix(currentHost, "https://") + currentHost = strings.TrimPrefix(currentHost, "http://") + if currentHost == "localhost:8080" { + u.Scheme = "http" + } else { + u.Scheme = "https" + } + u.Host = currentHost + return true, u + } + return false, nil +} + func (i *Scene) ImportScene(ctx context.Context, sce *scene.Scene, prj *project.Project, plgs []*plugin.Plugin, sceneData map[string]interface{}) (*scene.Scene, error) { sceneJSON, err := builder.ParseSceneJSON(ctx, sceneData) if err != nil { @@ -747,7 +770,7 @@ func (i *Scene) ImportScene(ctx context.Context, sce *scene.Scene, prj *project. if err != nil { return nil, err } - prop, err = builder.AddItemFromPropertyJSON(prop, ps, widgetJSON.Property) + prop, err = builder.AddItemFromPropertyJSON(ctx, prop, ps, widgetJSON.Property) if err != nil { return nil, err } @@ -791,7 +814,7 @@ func (i *Scene) ImportScene(ctx context.Context, sce *scene.Scene, prj *project. if err != nil { return nil, err } - prop, err = builder.AddItemFromPropertyJSON(prop, schema, sceneJSON.Property) + prop, err = builder.AddItemFromPropertyJSON(ctx, prop, schema, sceneJSON.Property) if err != nil { return nil, err } diff --git a/server/internal/usecase/interactor/storytelling.go b/server/internal/usecase/interactor/storytelling.go index 29ebfce53..3c64d2c05 100644 --- a/server/internal/usecase/interactor/storytelling.go +++ b/server/internal/usecase/interactor/storytelling.go @@ -1035,7 +1035,7 @@ func (i *Storytelling) ImportStory(ctx context.Context, sceneID idx.ID[id.Scene] if err != nil { return nil, err } - prop, err = builder.AddItemFromPropertyJSON(prop, ps, blockJSON.Property) + prop, err = builder.AddItemFromPropertyJSON(ctx, prop, ps, blockJSON.Property) if err != nil { return nil, err } @@ -1064,7 +1064,7 @@ func (i *Storytelling) ImportStory(ctx context.Context, sceneID idx.ID[id.Scene] if err != nil { return nil, err } - prop, err = builder.AddItemFromPropertyJSON(prop, ps, pageJSON.Property) + prop, err = builder.AddItemFromPropertyJSON(ctx, prop, ps, pageJSON.Property) if err != nil { return nil, err } @@ -1101,7 +1101,7 @@ func (i *Storytelling) ImportStory(ctx context.Context, sceneID idx.ID[id.Scene] if err != nil { return nil, err } - prop, err = builder.AddItemFromPropertyJSON(prop, ps, storyJSON.Property) + prop, err = builder.AddItemFromPropertyJSON(ctx, prop, ps, storyJSON.Property) if err != nil { return nil, err } diff --git a/server/pkg/scene/builder/decoder.go b/server/pkg/scene/builder/decoder.go index fc38201ad..3034e8f2e 100644 --- a/server/pkg/scene/builder/decoder.go +++ b/server/pkg/scene/builder/decoder.go @@ -3,12 +3,15 @@ package builder import ( "context" "encoding/json" - "fmt" + "net/url" + "strings" + "github.com/reearth/reearth/server/internal/adapter" "github.com/reearth/reearth/server/pkg/id" "github.com/reearth/reearth/server/pkg/property" "github.com/reearth/reearth/server/pkg/scene" "github.com/reearth/reearthx/idx" + "github.com/reearth/reearthx/log" ) func ParseSceneJSON(ctx context.Context, sceneJSONData map[string]interface{}) (*sceneJSON, error) { @@ -106,7 +109,7 @@ func parseWidgetAreaPadding(paddingJSON *widgetAreaPaddingJSON) *scene.WidgetAre ) } -func AddItemFromPropertyJSON(prop *property.Property, ps *property.Schema, pj propertyJSON) (*property.Property, error) { +func AddItemFromPropertyJSON(ctx context.Context, prop *property.Property, ps *property.Schema, pj propertyJSON) (*property.Property, error) { for sgKey, value := range pj { if items, ok := value.(map[string]interface{}); ok { @@ -118,7 +121,7 @@ func AddItemFromPropertyJSON(prop *property.Property, ps *property.Schema, pj pr fieldID := id.PropertyFieldIDFromRef(&fieldKey) ptr := property.NewPointer(sgID, nil, fieldID) - pv, ok := parsePropertyValue(value) + pv, ok := parsePropertyValue(ctx, value) if ok && ps != nil { _, _, _, err := prop.UpdateValue(ps, ptr, pv) @@ -144,7 +147,7 @@ func AddItemFromPropertyJSON(prop *property.Property, ps *property.Schema, pj pr if fieldKey == "id" { continue } - ov, ok := parsePropertyOptionalValue(value) + ov, ok := parsePropertyOptionalValue(ctx, value) if ok { fieldID := id.PropertyFieldIDFromRef(&fieldKey) field := property.NewField(*fieldID). @@ -161,20 +164,40 @@ func AddItemFromPropertyJSON(prop *property.Property, ps *property.Schema, pj pr return prop, nil } -func parsePropertyValue(value interface{}) (*property.Value, bool) { +func parsePropertyValue(ctx context.Context, value interface{}) (*property.Value, bool) { if fieldObj, ok := value.(map[string]interface{}); ok { - fieldType, ok1 := fieldObj["type"].(string) - fieldVal, ok2 := fieldObj["value"] - if ok1 && ok2 { - return property.ValueType(fieldType).ValueFrom(fieldVal), ok + if fieldType, ok := fieldObj["type"].(string); ok { + if fieldVal, ok := fieldObj["value"].(string); ok { + if fieldType == "url" { + urlVal, err := url.Parse(fieldVal) + if err != nil { + log.Infofc(ctx, "invalid url: %v\n", err.Error()) + return nil, false + } + if urlVal.Host == "localhost:8080" || strings.HasSuffix(urlVal.Host, ".reearth.dev") || strings.HasSuffix(urlVal.Host, ".reearth.io") { + currentHost := adapter.CurrentHost(ctx) + currentHost = strings.TrimPrefix(currentHost, "https://") + currentHost = strings.TrimPrefix(currentHost, "http://") + urlVal.Host = currentHost + if currentHost == "localhost:8080" { + urlVal.Scheme = "http" + } else { + urlVal.Scheme = "https" + } + fieldVal = urlVal.String() + + } + } + return property.ValueType(fieldType).ValueFrom(fieldVal), ok + } } } - fmt.Printf("property is unreadable %v\n", value) + log.Infofc(ctx, "property is unreadable %v\n", value) return nil, false } -func parsePropertyOptionalValue(value interface{}) (*property.OptionalValue, bool) { - pv, ok := parsePropertyValue(value) +func parsePropertyOptionalValue(ctx context.Context, value interface{}) (*property.OptionalValue, bool) { + pv, ok := parsePropertyValue(ctx, value) if ok { ov := property.NewOptionalValue(pv.Type(), pv) return ov, true