diff --git a/pkg/sdkserver/routes.go b/pkg/sdkserver/routes.go index dfad4a18..c4b45e92 100644 --- a/pkg/sdkserver/routes.go +++ b/pkg/sdkserver/routes.go @@ -3,7 +3,6 @@ package sdkserver import ( "encoding/json" "fmt" - "io" "net/http" "sort" "strings" @@ -81,6 +80,9 @@ func (s *server) addRoutes(mux *http.ServeMux) { mux.HandleFunc("POST /workspaces/delete-file", s.removeFileInWorkspace) mux.HandleFunc("POST /workspaces/read-file", s.readFileInWorkspace) mux.HandleFunc("POST /workspaces/stat-file", s.statFileInWorkspace) + mux.HandleFunc("POST /workspaces/list-revisions", s.listRevisions) + mux.HandleFunc("POST /workspaces/get-revision", s.getRevisionForFileInWorkspace) + mux.HandleFunc("POST /workspaces/delete-revision", s.deleteRevisionForFileInWorkspace) } // health just provides an endpoint for checking whether the server is running and accessible. @@ -152,14 +154,9 @@ func (s *server) listModels(w http.ResponseWriter, r *http.Request) { // Then the options and tool are passed to the process function. func (s *server) execHandler(w http.ResponseWriter, r *http.Request) { logger := gcontext.GetLogger(r.Context()) - body, err := io.ReadAll(r.Body) - if err != nil { - writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to read request body: %w", err)) - return - } reqObject := new(toolOrFileRequest) - if err := json.Unmarshal(body, reqObject); err != nil { + if err := json.NewDecoder(r.Body).Decode(reqObject); err != nil { writeError(logger, w, http.StatusBadRequest, fmt.Errorf("invalid request body: %w", err)) return } diff --git a/pkg/sdkserver/workspaces.go b/pkg/sdkserver/workspaces.go index f0d7ef00..bde59974 100644 --- a/pkg/sdkserver/workspaces.go +++ b/pkg/sdkserver/workspaces.go @@ -321,3 +321,112 @@ func (s *server) statFileInWorkspace(w http.ResponseWriter, r *http.Request) { writeResponse(logger, w, map[string]any{"stdout": out}) } + +type listRevisionsRequest struct { + workspaceCommonRequest `json:",inline"` + FilePath string `json:"filePath"` +} + +func (s *server) listRevisions(w http.ResponseWriter, r *http.Request) { + logger := gcontext.GetLogger(r.Context()) + var reqObject listRevisionsRequest + if err := json.NewDecoder(r.Body).Decode(&reqObject); err != nil { + writeError(logger, w, http.StatusBadRequest, fmt.Errorf("invalid request body: %w", err)) + } + + prg, err := loader.Program(r.Context(), s.getWorkspaceTool(reqObject.workspaceCommonRequest), "List Revisions for File in Workspace", loader.Options{Cache: s.client.Cache}) + if err != nil { + writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to load program: %w", err)) + return + } + + out, err := s.client.Run( + r.Context(), + prg, + s.getServerToolsEnv(reqObject.Env), + fmt.Sprintf( + `{"workspace_id": "%s", "file_path": "%s"}`, + reqObject.ID, reqObject.FilePath, + ), + ) + if err != nil { + writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to run program: %w", err)) + return + } + + writeResponse(logger, w, map[string]any{"stdout": out}) +} + +type getRevisionForFileInWorkspaceRequest struct { + workspaceCommonRequest `json:",inline"` + FilePath string `json:"filePath"` + RevisionID string `json:"revisionID"` +} + +func (s *server) getRevisionForFileInWorkspace(w http.ResponseWriter, r *http.Request) { + logger := gcontext.GetLogger(r.Context()) + var reqObject getRevisionForFileInWorkspaceRequest + if err := json.NewDecoder(r.Body).Decode(&reqObject); err != nil { + writeError(logger, w, http.StatusBadRequest, fmt.Errorf("invalid request body: %w", err)) + return + } + + prg, err := loader.Program(r.Context(), s.getWorkspaceTool(reqObject.workspaceCommonRequest), "Get a Revision for File in Workspace", loader.Options{Cache: s.client.Cache}) + if err != nil { + writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to load program: %w", err)) + return + } + + out, err := s.client.Run( + r.Context(), + prg, + s.getServerToolsEnv(reqObject.Env), + fmt.Sprintf( + `{"workspace_id": "%s", "file_path": "%s", "revision_id": "%s"}`, + reqObject.ID, reqObject.FilePath, reqObject.RevisionID, + ), + ) + if err != nil { + writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to run program: %w", err)) + return + } + + writeResponse(logger, w, map[string]any{"stdout": out}) +} + +type deleteRevisionForFileInWorkspaceRequest struct { + workspaceCommonRequest `json:",inline"` + FilePath string `json:"filePath"` + RevisionID string `json:"revisionID"` +} + +func (s *server) deleteRevisionForFileInWorkspace(w http.ResponseWriter, r *http.Request) { + logger := gcontext.GetLogger(r.Context()) + var reqObject deleteRevisionForFileInWorkspaceRequest + if err := json.NewDecoder(r.Body).Decode(&reqObject); err != nil { + writeError(logger, w, http.StatusBadRequest, fmt.Errorf("invalid request body: %w", err)) + return + } + + prg, err := loader.Program(r.Context(), s.getWorkspaceTool(reqObject.workspaceCommonRequest), "Delete a Revision for File in Workspace", loader.Options{Cache: s.client.Cache}) + if err != nil { + writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to load program: %w", err)) + return + } + + out, err := s.client.Run( + r.Context(), + prg, + s.getServerToolsEnv(reqObject.Env), + fmt.Sprintf( + `{"workspace_id": "%s", "file_path": "%s", "revision_id": "%s"}`, + reqObject.ID, reqObject.FilePath, reqObject.RevisionID, + ), + ) + if err != nil { + writeError(logger, w, http.StatusInternalServerError, fmt.Errorf("failed to run program: %w", err)) + return + } + + writeResponse(logger, w, map[string]any{"stdout": out}) +}