oto/services/core/internal/httpserver/dto.go
toki e90ba93eab refactor(core): 코어 경계를 정리한다
클라이언트 계약과 서버 핸들러 경계를 분리해 변경 범위를 명확히 하고, 장기 테스트 흐름에 맞춰 문서와 경로 정리를 함께 반영한다.
2026-06-08 18:59:55 +09:00

120 lines
3.4 KiB
Go

package httpserver
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/toki/oto/services/core/internal/cicdstate"
otopb "github.com/toki/oto/services/core/oto"
)
func jobToJSON(j *cicdstate.Job) map[string]interface{} {
out := map[string]interface{}{
"id": j.ID,
"name": j.Name,
"state": j.State,
"created_at": j.CreatedAt.Format(time.RFC3339),
"updated_at": j.UpdatedAt.Format(time.RFC3339),
"execution_id": j.ExecutionID,
}
if j.RunInput != nil {
out["run_request"] = runInputToJSON(j.RunInput)
}
return out
}
// validateRunInput enforces the remote claim contract: exactly one of
// pipeline_yaml or pipeline_yaml_path must be provided.
func validateRunInput(in *cicdstate.RunInput) error {
hasYAML := strings.TrimSpace(in.PipelineYAML) != ""
hasPath := strings.TrimSpace(in.PipelineYAMLPath) != ""
if hasYAML == hasPath {
return fmt.Errorf("run request must set exactly one of pipeline_yaml or pipeline_yaml_path")
}
return nil
}
// runRequestFromJSON converts an incoming otopb.RunRequest decoded from the job
// create body into a store-level RunInput. It returns nil when no run request
// was supplied, keeping job creation backward compatible.
func runRequestFromJSON(rr *otopb.RunRequest) *cicdstate.RunInput {
if rr == nil {
return nil
}
in := &cicdstate.RunInput{
PipelineYAMLPath: rr.GetPipelineYamlPath(),
PipelineYAML: rr.GetPipelineYaml(),
CommandTypes: append([]string(nil), rr.GetCommandTypes()...),
}
if len(rr.GetVariables()) > 0 {
in.Variables = make(map[string]string, len(rr.GetVariables()))
for k, v := range rr.GetVariables() {
in.Variables[k] = v
}
}
return in
}
// runRequestToProto builds the claim response RunRequest from the stored input,
// filling in the runner/job/execution identifiers resolved at claim time.
func runRequestToProto(in *cicdstate.RunInput, runnerID, jobID, execID string) *otopb.RunRequest {
if in == nil {
return nil
}
rr := &otopb.RunRequest{
RunnerId: runnerID,
JobId: jobID,
ExecutionId: execID,
PipelineYamlPath: in.PipelineYAMLPath,
PipelineYaml: in.PipelineYAML,
CommandTypes: append([]string(nil), in.CommandTypes...),
}
if len(in.Variables) > 0 {
rr.Variables = make(map[string]string, len(in.Variables))
for k, v := range in.Variables {
rr.Variables[k] = v
}
}
return rr
}
func runInputToJSON(in *cicdstate.RunInput) map[string]interface{} {
return map[string]interface{}{
"pipeline_yaml_path": in.PipelineYAMLPath,
"pipeline_yaml": in.PipelineYAML,
"variables": in.Variables,
"command_types": in.CommandTypes,
}
}
func execToJSON(e *cicdstate.Execution) map[string]interface{} {
return map[string]interface{}{
"id": e.ID,
"job_id": e.JobID,
"state": e.State,
"created_at": e.CreatedAt.Format(time.RFC3339),
"updated_at": e.UpdatedAt.Format(time.RFC3339),
"execution_id": e.ID,
}
}
func logsToJSON(logs []cicdstate.LogEntry) map[string]interface{} {
return map[string]interface{}{
"logs": logs,
}
}
func artifactsToJSON(artifacts []cicdstate.ArtifactEntry) map[string]interface{} {
return map[string]interface{}{
"artifacts": artifacts,
}
}
func writeResponse(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(data)
}