Skip to content

Commit

Permalink
fix: proper API JSON validation (#905)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: The swagger definition changed significantly and now all request objects must be properly structured

Signed-off-by: Toma Puljak <[email protected]>
  • Loading branch information
Tpuljak authored Aug 13, 2024
1 parent 72504f8 commit cd21e05
Show file tree
Hide file tree
Showing 170 changed files with 4,405 additions and 3,489 deletions.
1 change: 0 additions & 1 deletion .github/workflows/lint_pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ jobs:
run: |
go install github.com/swaggo/swag/cmd/[email protected]
./hack/swagger.sh
go fmt ./...
git diff --exit-code pkg/apiclient/* || (echo "API clients are out of sync! Please generate with './hack/swagger.sh' and commit" && exit 1)
git diff --exit-code '**/*.go' || (echo "Code is not formatted! Please run 'go fmt ./...' and commit" && exit 1)
cli-docs:
Expand Down
7 changes: 6 additions & 1 deletion hack/swagger.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
# SPDX-License-Identifier: Apache-2.0

(cd pkg && swag fmt && swag init --parseDependency --parseInternal --parseDepth 1 -o api/docs -g api/server.go)
GO_POST_PROCESS_FILE="/usr/local/bin/gofmt -w" GIT_USER_ID=daytonaio GIT_REPO_ID=daytona npx --yes @openapitools/openapi-generator-cli generate -i pkg/api/docs/swagger.json -g go --package-name=apiclient --additional-properties=isGoSubmodule=true -o pkg/apiclient && rm -rf pkg/apiclient/.openapi-generator/FILES

rm pkg/apiclient/*.go
rm -rf pkg/apiclient/docs

GO_POST_PROCESS_FILE="/usr/local/bin/gofmt -w" GIT_USER_ID=daytonaio GIT_REPO_ID=daytona npx --yes @openapitools/openapi-generator-cli generate -i pkg/api/docs/swagger.json -g go --package-name=apiclient --additional-properties=isGoSubmodule=true -o pkg/apiclient && rm -rf pkg/apiclient/.openapi-generator/FILES
go fmt ./...
9 changes: 6 additions & 3 deletions internal/cmd/tailscale/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@ func GetConnection(profile *config.Profile) (*tsnet.Server, error) {

var controlURL string
if strings.Contains(profile.Api.Url, "localhost") || strings.Contains(profile.Api.Url, "0.0.0.0") || strings.Contains(profile.Api.Url, "127.0.0.1") {
controlURL = fmt.Sprintf("http://localhost:%d", *serverConfig.HeadscalePort)
controlURL = fmt.Sprintf("http://localhost:%d", serverConfig.HeadscalePort)
} else {
controlURL = util.GetFrpcHeadscaleUrl(*serverConfig.Frps.Protocol, *serverConfig.Id, *serverConfig.Frps.Domain)
if serverConfig.Frps == nil {
return nil, fmt.Errorf("frps config is missing")
}
controlURL = util.GetFrpcHeadscaleUrl(serverConfig.Frps.Protocol, serverConfig.Id, serverConfig.Frps.Domain)
}

return tailscale.GetConnection(&tailscale.TsnetConnConfig{
AuthKey: *networkKey.Key,
AuthKey: networkKey.Key,
ControlURL: controlURL,
Dir: filepath.Join(configDir, "tailscale", cliId),
Logf: func(format string, args ...any) {},
Expand Down
6 changes: 3 additions & 3 deletions internal/util/apiclient/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ func GetFirstWorkspaceProjectName(workspaceId string, projectName string, profil
return "", errors.New("no projects found in workspace")
}

return *wsInfo.Projects[0].Name, nil
return wsInfo.Projects[0].Name, nil
}

for _, project := range wsInfo.Projects {
if *project.Name == projectName {
return *project.Name, nil
if project.Name == projectName {
return project.Name, nil
}
}

Expand Down
56 changes: 26 additions & 30 deletions internal/util/apiclient/conversion/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ func ToProject(projectDTO *apiclient.Project) *project.Project {
}

repository := &gitprovider.GitRepository{
Id: *projectDTO.Repository.Id,
Name: *projectDTO.Repository.Name,
Id: projectDTO.Repository.Id,
Name: projectDTO.Repository.Name,
Branch: projectDTO.Repository.Branch,
Owner: *projectDTO.Repository.Owner,
Owner: projectDTO.Repository.Owner,
Path: projectDTO.Repository.Path,
Sha: *projectDTO.Repository.Sha,
Source: *projectDTO.Repository.Source,
Url: *projectDTO.Repository.Url,
Sha: projectDTO.Repository.Sha,
Source: projectDTO.Repository.Source,
Url: projectDTO.Repository.Url,
}

var projectState *project.ProjectState
if projectDTO.State != nil {
uptime := *projectDTO.State.Uptime
uptime := projectDTO.State.Uptime
projectState = &project.ProjectState{
UpdatedAt: *projectDTO.State.UpdatedAt,
UpdatedAt: projectDTO.State.UpdatedAt,
Uptime: uint64(uptime),
GitStatus: ToGitStatus(projectDTO.State.GitStatus),
}
Expand All @@ -41,23 +41,23 @@ func ToProject(projectDTO *apiclient.Project) *project.Project {
var projectBuild *buildconfig.ProjectBuildConfig
if projectDTO.BuildConfig != nil {
projectBuild = &buildconfig.ProjectBuildConfig{}
if projectDTO.BuildConfig.Devcontainer != nil && projectDTO.BuildConfig.Devcontainer.FilePath != nil {
if projectDTO.BuildConfig.Devcontainer != nil {
projectBuild.Devcontainer = &buildconfig.DevcontainerConfig{
FilePath: *projectDTO.BuildConfig.Devcontainer.FilePath,
FilePath: projectDTO.BuildConfig.Devcontainer.FilePath,
}
}
}

project := &project.Project{
ProjectConfig: config.ProjectConfig{
Name: *projectDTO.Name,
Image: *projectDTO.Image,
User: *projectDTO.User,
Name: projectDTO.Name,
Image: projectDTO.Image,
User: projectDTO.User,
BuildConfig: projectBuild,
Repository: repository,
},
Target: *projectDTO.Target,
WorkspaceId: *projectDTO.WorkspaceId,
Target: projectDTO.Target,
WorkspaceId: projectDTO.WorkspaceId,
State: projectState,
}

Expand All @@ -69,26 +69,22 @@ func ToProject(projectDTO *apiclient.Project) *project.Project {
return project
}

func ToGitStatus(gitStatusDTO *apiclient.GitStatus) *project.GitStatus {
if gitStatusDTO == nil {
return nil
}

func ToGitStatus(gitStatusDTO apiclient.GitStatus) *project.GitStatus {
files := []*project.FileStatus{}
for _, fileDTO := range gitStatusDTO.FileStatus {
staging := project.Status(string(*fileDTO.Staging))
worktree := project.Status(string(*fileDTO.Worktree))
staging := project.Status(string(fileDTO.Staging))
worktree := project.Status(string(fileDTO.Worktree))
file := &project.FileStatus{
Name: *fileDTO.Name,
Extra: *fileDTO.Extra,
Name: fileDTO.Name,
Extra: fileDTO.Extra,
Staging: staging,
Worktree: worktree,
}
files = append(files, file)
}

return &project.GitStatus{
CurrentBranch: *gitStatusDTO.CurrentBranch,
CurrentBranch: gitStatusDTO.CurrentBranch,
Files: files,
}
}
Expand All @@ -103,16 +99,16 @@ func ToGitStatusDTO(gitStatus *project.GitStatus) *apiclient.GitStatus {
staging := apiclient.Status(string(file.Staging))
worktree := apiclient.Status(string(file.Worktree))
fileDTO := apiclient.FileStatus{
Name: &file.Name,
Extra: &file.Extra,
Staging: &staging,
Worktree: &worktree,
Name: file.Name,
Extra: file.Extra,
Staging: staging,
Worktree: worktree,
}
fileStatusDTO = append(fileStatusDTO, fileDTO)
}

return &apiclient.GitStatus{
CurrentBranch: &gitStatus.CurrentBranch,
CurrentBranch: gitStatus.CurrentBranch,
FileStatus: fileStatusDTO,
}
}
Expand Down
24 changes: 10 additions & 14 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,8 @@ func (a *Agent) startProjectMode() error {
var auth *http.BasicAuth
if gitProvider != nil {
auth = &http.BasicAuth{}
if gitProvider.Username != nil {
auth.Username = *gitProvider.Username
}
if gitProvider.Token != nil {
auth.Password = *gitProvider.Token
}
auth.Username = gitProvider.Username
auth.Password = gitProvider.Token
}

exists, err := a.Git.RepositoryExists(project)
Expand Down Expand Up @@ -103,15 +99,15 @@ func (a *Agent) startProjectMode() error {

var gitUser *gitprovider.GitUser
if gitProvider != nil {
user, err := a.getGitUser(*gitProvider.Id)
user, err := a.getGitUser(gitProvider.Id)
if err != nil {
log.Error(fmt.Sprintf("failed to get git user data: %s", err))
} else {
gitUser = &gitprovider.GitUser{
Email: *user.Email,
Name: *user.Name,
Id: *user.Id,
Username: *user.Username,
Email: user.Email,
Name: user.Name,
Id: user.Id,
Username: user.Username,
}
}
}
Expand Down Expand Up @@ -149,7 +145,7 @@ func (a *Agent) getProject() (*project.Project, error) {
}

for _, project := range workspace.Projects {
if *project.Name == a.Config.ProjectName {
if project.Name == a.Config.ProjectName {
return conversion.ToProject(&project), nil
}
}
Expand Down Expand Up @@ -228,7 +224,7 @@ func (a *Agent) setDefaultConfig() error {

// Agent uptime in seconds
func (a *Agent) uptime() int32 {
return int32(time.Since(a.startTime).Seconds())
return max(int32(time.Since(a.startTime).Seconds()), 1)
}

func (a *Agent) updateProjectState() error {
Expand All @@ -244,7 +240,7 @@ func (a *Agent) updateProjectState() error {

uptime := a.uptime()
res, err := apiClient.WorkspaceAPI.SetProjectState(context.Background(), a.Config.WorkspaceId, a.Config.ProjectName).SetState(apiclient.SetProjectState{
Uptime: &uptime,
Uptime: uptime,
GitStatus: conversion.ToGitStatusDTO(gitStatus),
}).Execute()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/agent/tailscale/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (s *Server) getNetworkKey() (string, error) {
return s.getNetworkKey()
}

return *networkKey.Key, nil
return networkKey.Key, nil
}

func (s *Server) getTsnetServer() (*tsnet.Server, error) {
Expand Down
15 changes: 15 additions & 0 deletions pkg/api/controllers/gitprovider/dto/dto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2024 Daytona Platforms Inc.
// SPDX-License-Identifier: Apache-2.0

package dto

type RepositoryUrl struct {
URL string `json:"url" validate:"required"`
} // @name RepositoryUrl

type SetGitProviderConfig struct {
Id string `json:"id" validate:"required"`
Username *string `json:"username" validate:"optional"`
Token string `json:"token" validate:"required"`
BaseApiUrl *string `json:"baseApiUrl,omitempty" validate:"optional"`
} // @name SetGitProviderConfig
8 changes: 0 additions & 8 deletions pkg/api/controllers/gitprovider/dto/repository_url.go

This file was deleted.

19 changes: 15 additions & 4 deletions pkg/api/controllers/gitprovider/gitprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"net/url"

"github.com/daytonaio/daytona/pkg/api/controllers/gitprovider/dto"
"github.com/daytonaio/daytona/pkg/gitprovider"
"github.com/daytonaio/daytona/pkg/server"
"github.com/gin-gonic/gin"
Expand Down Expand Up @@ -109,24 +110,34 @@ func GetGitProviderIdForUrl(ctx *gin.Context) {
// @Tags gitProvider
// @Summary Set Git provider
// @Description Set Git provider
// @Param gitProviderConfig body gitprovider.GitProviderConfig true "Git provider"
// @Param gitProviderConfig body SetGitProviderConfig true "Git provider"
// @Produce json
// @Success 200
// @Router /gitprovider [put]
//
// @id SetGitProvider
func SetGitProvider(ctx *gin.Context) {
var gitProviderData gitprovider.GitProviderConfig
var setConfigDto dto.SetGitProviderConfig

err := ctx.BindJSON(&gitProviderData)
err := ctx.BindJSON(&setConfigDto)
if err != nil {
ctx.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid request body: %s", err.Error()))
return
}

gitProviderConfig := gitprovider.GitProviderConfig{
Id: setConfigDto.Id,
Token: setConfigDto.Token,
BaseApiUrl: setConfigDto.BaseApiUrl,
}

if setConfigDto.Username != nil {
gitProviderConfig.Username = *setConfigDto.Username
}

server := server.GetInstance(nil)

err = server.GitProviderService.SetGitProviderConfig(&gitProviderData)
err = server.GitProviderService.SetGitProviderConfig(&gitProviderConfig)
if err != nil {
ctx.AbortWithError(http.StatusBadRequest, fmt.Errorf("failed to set git provider: %s", err.Error()))
return
Expand Down
8 changes: 4 additions & 4 deletions pkg/api/controllers/provider/dto/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

type Provider struct {
Name string `json:"name"`
Version string `json:"version"`
Name string `json:"name" validate:"required"`
Version string `json:"version" validate:"required"`
} // @name Provider

type InstallProviderRequest struct {
Name string `json:"name"`
DownloadUrls map[os.OperatingSystem]string `json:"downloadUrls"`
Name string `json:"name" validate:"required"`
DownloadUrls map[os.OperatingSystem]string `json:"downloadUrls" validate:"required"`
} // @name InstallProviderRequest
4 changes: 2 additions & 2 deletions pkg/api/controllers/workspace/dto/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import (
)

type SetProjectState struct {
Uptime uint64 `json:"uptime"`
GitStatus project.GitStatus `json:"gitStatus"`
Uptime uint64 `json:"uptime" validate:"required"`
GitStatus *project.GitStatus `json:"gitStatus,omitempty" validate:"optional"`
} // @name SetProjectState
2 changes: 1 addition & 1 deletion pkg/api/controllers/workspace/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func SetProjectState(ctx *gin.Context) {
_, err = server.WorkspaceService.SetProjectState(workspaceId, projectId, &project.ProjectState{
Uptime: setProjectStateDTO.Uptime,
UpdatedAt: time.Now().Format(time.RFC1123),
GitStatus: &setProjectStateDTO.GitStatus,
GitStatus: setProjectStateDTO.GitStatus,
})
if err != nil {
ctx.AbortWithError(http.StatusInternalServerError, fmt.Errorf("failed to stop workspace %s: %s", workspaceId, err.Error()))
Expand Down
Loading

0 comments on commit cd21e05

Please sign in to comment.