diff --git a/cmd/sync.go b/cmd/sync.go index 9215edf..bcd1b83 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -852,6 +852,7 @@ func runSync(cmd *cobra.Command, args []string) error { X: fp.X, Y: fp.Y, Visibility: visibility, + Color: fp.Color, CommitHash: gitMeta.CommitHash, }) if err != nil { @@ -892,6 +893,19 @@ func runSync(cmd *cobra.Command, args []string) error { fmt.Printf(" ✓ Component: %s\n", comp.ComponentID) } } + + publishResp, err := client.PublishFrame(ctx, gateway.FramePublishRequest{ + MapName: m.Name, + FrameName: frame.Name, + }) + if err != nil { + exitGatewayError(fmt.Sprintf("publish version for frame %q", frame.Name)) + } + if publishResp.VersionCreated { + fmt.Printf(" ✓ Version %d published\n", publishResp.VersionNumber) + } else { + fmt.Printf(" ✓ No changes since version %d\n", publishResp.VersionNumber) + } } } } diff --git a/pkg/config/config.go b/pkg/config/config.go index 938a17c..d9e6170 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -141,9 +141,24 @@ type FocalPointRef struct { X float64 `yaml:"x"` Y float64 `yaml:"y"` Visibility string `yaml:"visibility,omitempty"` + Color *string `yaml:"color,omitempty"` Components []FocalPointMetaRef `yaml:"components,omitempty"` } +func (f *FocalPointRef) UnmarshalYAML(node *yaml.Node) error { + type plain FocalPointRef + if err := node.Decode((*plain)(f)); err != nil { + return err + } + for i := 0; i+1 < len(node.Content); i += 2 { + if node.Content[i].Value == "color" && node.Content[i+1].ShortTag() == "!!null" { + empty := "" + f.Color = &empty + } + } + return nil +} + type FocalPointMetaRef struct { ComponentID string `yaml:"componentId"` @@ -659,6 +674,7 @@ func (c *Config) Validate() error { "component_support-kb-troubleshooting": true, "component_backend-flow-diagram": true, } + hexColor := regexp.MustCompile("^#[0-9a-fA-F]{6}$") for i, m := range c.Maps { if m.Name == "" { problems = append(problems, fmt.Sprintf("maps[%d].name is required", i)) @@ -676,6 +692,9 @@ func (c *Config) Validate() error { if fp.Name == "" { problems = append(problems, fmt.Sprintf("maps[%d].frames[%d].focalPoints[%d].name is required", i, j, k)) } + if fp.Color != nil && *fp.Color != "" && !hexColor.MatchString(*fp.Color) { + problems = append(problems, fmt.Sprintf("maps[%d].frames[%d].focalPoints[%d].color must be a hex color like \"#FF5733\"", i, j, k)) + } for l, comp := range fp.Components { if comp.ComponentID == "" { problems = append(problems, fmt.Sprintf("maps[%d].frames[%d].focalPoints[%d].components[%d].componentId is required", i, j, k, l)) diff --git a/pkg/gateway/client.go b/pkg/gateway/client.go index d929cbd..b2b734c 100644 --- a/pkg/gateway/client.go +++ b/pkg/gateway/client.go @@ -330,6 +330,7 @@ type FocalPointSyncRequest struct { X float64 `json:"x"` Y float64 `json:"y"` Visibility string `json:"visibility,omitempty"` + Color *string `json:"color,omitempty"` CommitHash string `json:"commitHash,omitempty"` } @@ -339,6 +340,16 @@ type FocalPointSyncResponse struct { Message string `json:"message"` } +type FramePublishRequest struct { + MapName string `json:"mapName"` + FrameName string `json:"frameName"` +} + +type FramePublishResponse struct { + VersionCreated bool `json:"versionCreated"` + VersionNumber int `json:"versionNumber"` +} + type ComponentFieldItem struct { ComponentFieldID string `json:"componentFieldId"` Label string `json:"label"` @@ -828,6 +839,45 @@ func (c *Client) SyncFocalPoint(ctx context.Context, req FocalPointSyncRequest) return &syncResp, nil } +func (c *Client) PublishFrame(ctx context.Context, req FramePublishRequest) (*FramePublishResponse, error) { + url := fmt.Sprintf("%s/v1/sync/frame/publish", c.baseURL) + + body, err := json.Marshal(req) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } + + httpReq, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + httpReq.Header.Set("Content-Type", "application/json") + httpReq.Header.Set("X-API-Token", c.token) + + resp, err := c.httpClient.Do(httpReq) + if err != nil { + return nil, fmt.Errorf("request failed: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + if resp.StatusCode >= 400 { + return nil, formatGatewayError(resp.StatusCode, respBody) + } + + var publishResp FramePublishResponse + if err := json.Unmarshal(respBody, &publishResp); err != nil { + return nil, fmt.Errorf("failed to parse response: %w", err) + } + + return &publishResp, nil +} + func (c *Client) SyncFocalPointMeta(ctx context.Context, req FocalPointMetaSyncRequest) (*FocalPointMetaSyncResponse, error) { url := fmt.Sprintf("%s/v1/sync/focal-point-meta", c.baseURL)