Skip to content

Commit 8f6d6bf

Browse files
committed
Create workspace from git
1 parent e301e0b commit 8f6d6bf

14 files changed

+466
-39
lines changed

app/client/workspace/workspace_service.go

+49
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,55 @@ type WorkspaceServiceClient struct {
2121
slackAlert *monitoring.SlackAlert
2222
}
2323

24+
func (ws *WorkspaceServiceClient) ImportGitRepository(importRepository *request.ImportGitRepository) (createWorkspaceResponse *response.CreateWorkspaceResponse, err error) {
25+
payload, err := json.Marshal(importRepository)
26+
if err != nil {
27+
log.Printf("failed to marshal import git repository request: %v", err)
28+
return
29+
}
30+
31+
req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v1/workspaces/import", ws.endpoint), bytes.NewBuffer(payload))
32+
if err != nil {
33+
log.Printf("failed to create import git repository request: %v", err)
34+
return
35+
}
36+
37+
res, err := ws.client.Do(req)
38+
if err != nil {
39+
log.Printf("failed to send import git repository request: %v", err)
40+
return
41+
}
42+
43+
if res.StatusCode < 200 || res.StatusCode > 299 {
44+
err := ws.slackAlert.SendAlert(fmt.Sprintf("failed to import git repository: %s", res.Status), map[string]string{
45+
"workspace_id": importRepository.WorkspaceId,
46+
"repository": importRepository.Repository,
47+
})
48+
if err != nil {
49+
log.Printf("failed to send slack alert: %v", err)
50+
return nil, err
51+
}
52+
return nil, errors.New(fmt.Sprintf("invalid res from workspace service for import git repository request"))
53+
}
54+
55+
defer func(Body io.ReadCloser) {
56+
_ = Body.Close()
57+
}(res.Body)
58+
59+
responseBody, err := io.ReadAll(res.Body)
60+
if err != nil {
61+
log.Printf("failed to read res payload: %v", err)
62+
return
63+
}
64+
65+
createWorkspaceResponse = &response.CreateWorkspaceResponse{}
66+
if err = json.Unmarshal(responseBody, &createWorkspaceResponse); err != nil {
67+
log.Printf("failed to unmarshal create workspace res: %v", err)
68+
return
69+
}
70+
return
71+
}
72+
2473
func (ws *WorkspaceServiceClient) CreateWorkspace(createWorkspaceRequest *request.CreateWorkspaceRequest) (createWorkspaceResponse *response.CreateWorkspaceResponse, err error) {
2574
payload, err := json.Marshal(createWorkspaceRequest)
2675
if err != nil {

app/controllers/github_integration_controller.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,9 @@ func (gic *GithubIntegrationController) GetRepositories(c *gin.Context) {
4242
response := make([]map[string]interface{}, 0)
4343
for _, repo := range repositories {
4444
response = append(response, map[string]interface{}{
45-
"id": repo.GetID(),
46-
"name": repo.GetName(),
47-
"url": repo.GetURL(),
48-
"full_name": repo.GetFullName(),
45+
"id": repo.GetID(),
46+
"url": repo.GetCloneURL(),
47+
"name": repo.GetFullName(),
4948
})
5049
}
5150
c.JSON(http.StatusOK, gin.H{"repositories": response})

app/controllers/project_controller.go

+38
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,44 @@ func (controller *ProjectController) GetProjectById(context *gin.Context) {
5656
context.JSON(http.StatusOK, project)
5757
}
5858

59+
func (controller *ProjectController) CreateProjectFromGit(context *gin.Context) {
60+
var createProjectRequest request.CreateProjectFromGitRequest
61+
if err := context.ShouldBindJSON(&createProjectRequest); err != nil {
62+
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
63+
return
64+
}
65+
email, _ := context.Get("email")
66+
user, err := controller.userService.GetUserByEmail(email.(string))
67+
if err != nil {
68+
context.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
69+
return
70+
}
71+
if user == nil {
72+
context.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "User not found"})
73+
return
74+
}
75+
76+
project, err := controller.projectService.CreateProjectFromGit(user.ID, user.OrganisationID, createProjectRequest)
77+
if err != nil {
78+
context.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
79+
return
80+
}
81+
82+
context.JSON(
83+
http.StatusOK,
84+
gin.H{
85+
"project_id": project.ID,
86+
"project_url": project.Url,
87+
"project_name": project.Name,
88+
"project_frontend_url": project.FrontendURL,
89+
"project_backend_url": project.BackendURL,
90+
"project_framework": project.BackendFramework,
91+
"project_frontend_framework": project.FrontendFramework,
92+
},
93+
)
94+
return
95+
}
96+
5997
func (controller *ProjectController) CreateProject(context *gin.Context) {
6098
var createProjectRequest request.CreateProjectRequest
6199
if err := context.ShouldBindJSON(&createProjectRequest); err != nil {

app/services/integrations/github_integration_service.go

-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package integrations
33
import (
44
"ai-developer/app/config"
55
"ai-developer/app/models/dtos/integrations"
6-
"ai-developer/app/services"
76
"ai-developer/app/utils"
87
"context"
98
"fmt"
@@ -20,7 +19,6 @@ type GithubIntegrationService struct {
2019
oauthConfig oauth2.Config
2120
githubIntegrationConfig *config.GithubIntegrationConfig
2221

23-
userService *services.UserService
2422
integrationService *IntegrationService
2523
}
2624

app/services/project_service.go

+105-20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"ai-developer/app/models/dtos/asynq_task"
99
"ai-developer/app/repositories"
1010
"ai-developer/app/services/git_providers"
11+
"ai-developer/app/services/integrations"
1112
"ai-developer/app/types/request"
1213
"ai-developer/app/types/response"
1314
"ai-developer/app/utils"
@@ -22,16 +23,17 @@ import (
2223
)
2324

2425
type ProjectService struct {
25-
redisRepo *repositories.ProjectConnectionsRepository
26-
projectRepo *repositories.ProjectRepository
27-
organisationRepository *repositories.OrganisationRepository
28-
storyRepository *repositories.StoryRepository
29-
pullRequestRepository *repositories.PullRequestRepository
30-
gitnessService *git_providers.GitnessService
31-
hashIdGenerator *utils.HashIDGenerator
32-
workspaceServiceClient *workspace.WorkspaceServiceClient
33-
asynqClient *asynq.Client
34-
logger *zap.Logger
26+
redisRepo *repositories.ProjectConnectionsRepository
27+
projectRepo *repositories.ProjectRepository
28+
organisationRepository *repositories.OrganisationRepository
29+
storyRepository *repositories.StoryRepository
30+
pullRequestRepository *repositories.PullRequestRepository
31+
gitnessService *git_providers.GitnessService
32+
hashIdGenerator *utils.HashIDGenerator
33+
workspaceServiceClient *workspace.WorkspaceServiceClient
34+
asynqClient *asynq.Client
35+
githubIntegrationService *integrations.GithubIntegrationService
36+
logger *zap.Logger
3537
}
3638

3739
func (s *ProjectService) GetAllProjectsOfOrganisation(organisationId int) ([]response.GetAllProjectsResponse, error) {
@@ -82,6 +84,87 @@ func (s *ProjectService) GetProjectDetailsById(projectId int) (*models.Project,
8284
return project, nil
8385
}
8486

87+
func (s *ProjectService) CreateProjectFromGit(userId uint, orgId uint, requestData request.CreateProjectFromGitRequest) (project *models.Project, err error) {
88+
integrationDetails, err := s.githubIntegrationService.GetGithubIntegrationDetails(uint64(userId))
89+
if err != nil {
90+
s.logger.Error("Error getting github integration details", zap.Error(err))
91+
return nil, err
92+
}
93+
94+
hashID := s.hashIdGenerator.Generate() + "-" + uuid.New().String()
95+
url := "http://localhost:8081/?folder=/workspaces/" + hashID
96+
backend_url := "http://localhost:5000"
97+
frontend_url := "http://localhost:3000"
98+
env := config.Get("app.env")
99+
host := config.Get("workspace.host")
100+
if env == "production" {
101+
url = fmt.Sprintf("https://%s.%s/?folder=/workspaces/%s", hashID, host, hashID)
102+
backend_url = fmt.Sprintf("https://be-%s.%s", hashID, host)
103+
frontend_url = fmt.Sprintf("https://fe-%s.%s", hashID, host)
104+
}
105+
project = &models.Project{
106+
OrganisationID: orgId,
107+
Name: requestData.Name,
108+
BackendFramework: requestData.Framework,
109+
FrontendFramework: requestData.FrontendFramework,
110+
Description: requestData.Description,
111+
HashID: hashID,
112+
Url: url,
113+
BackendURL: backend_url,
114+
FrontendURL: frontend_url,
115+
}
116+
117+
organisation, err := s.organisationRepository.GetOrganisationByID(uint(int(project.OrganisationID)))
118+
spaceOrProjectName := s.gitnessService.GetSpaceOrProjectName(organisation)
119+
repository, err := s.gitnessService.CreateRepository(spaceOrProjectName, project.Name, project.Description)
120+
if err != nil {
121+
s.logger.Error("Error creating repository", zap.Error(err))
122+
return nil, err
123+
}
124+
httpPrefix := "https"
125+
126+
if config.AppEnv() == constants.Development {
127+
httpPrefix = "http"
128+
}
129+
130+
remoteGitURL := fmt.Sprintf("%s://%s:%s@%s/git/%s/%s.git", httpPrefix, config.GitnessUser(), config.GitnessToken(), config.GitnessHost(), spaceOrProjectName, project.Name)
131+
//Making Call to Workspace Service to create workspace on project level
132+
_, err = s.workspaceServiceClient.ImportGitRepository(
133+
&request.ImportGitRepository{
134+
WorkspaceId: hashID,
135+
Repository: requestData.Repository,
136+
Username: integrationDetails.GithubUserId,
137+
Password: integrationDetails.AccessToken,
138+
RemoteURL: remoteGitURL,
139+
GitnessUser: config.GitnessUser(),
140+
GitnessToken: config.GitnessToken(),
141+
},
142+
)
143+
144+
if err != nil {
145+
s.logger.Error("Error creating workspace", zap.Error(err))
146+
return nil, err
147+
}
148+
149+
//Enqueue job to delete workspace with updated delay
150+
payloadBytes, err := json.Marshal(asynq_task.CreateDeleteWorkspaceTaskPayload{
151+
WorkspaceID: project.HashID,
152+
})
153+
if err != nil {
154+
s.logger.Error("Failed to marshal payload", zap.Error(err))
155+
return nil, err
156+
}
157+
_, err = s.asynqClient.Enqueue(
158+
asynq.NewTask(constants.DeleteWorkspaceTaskType, payloadBytes),
159+
asynq.ProcessIn(constants.ProjectConnectionTTL+10*time.Minute),
160+
asynq.MaxRetry(3),
161+
asynq.TaskID("delete:fallback:"+project.HashID),
162+
)
163+
164+
s.logger.Info("Project created successfully with repository", zap.Any("project", project), zap.Any("repository", repository))
165+
return s.projectRepo.CreateProject(project)
166+
}
167+
85168
func (s *ProjectService) CreateProject(organisationID int, requestData request.CreateProjectRequest) (*models.Project, error) {
86169
hashID := s.hashIdGenerator.Generate() + "-" + uuid.New().String()
87170
url := "http://localhost:8081/?folder=/workspaces/" + hashID
@@ -314,21 +397,23 @@ func NewProjectService(projectRepo *repositories.ProjectRepository,
314397
storyRepository *repositories.StoryRepository,
315398
pullRequestRepository *repositories.PullRequestRepository,
316399
workspaceServiceClient *workspace.WorkspaceServiceClient,
400+
githubIntegrationService *integrations.GithubIntegrationService,
317401
repo *repositories.ProjectConnectionsRepository,
318402
asynqClient *asynq.Client,
319403
logger *zap.Logger,
320404
) *ProjectService {
321405
return &ProjectService{
322-
projectRepo: projectRepo,
323-
gitnessService: gitnessService,
324-
organisationRepository: organisationRepository,
325-
storyRepository: storyRepository,
326-
pullRequestRepository: pullRequestRepository,
327-
workspaceServiceClient: workspaceServiceClient,
328-
redisRepo: repo,
329-
hashIdGenerator: utils.NewHashIDGenerator(5),
330-
logger: logger.Named("ProjectService"),
331-
asynqClient: asynqClient,
406+
projectRepo: projectRepo,
407+
gitnessService: gitnessService,
408+
organisationRepository: organisationRepository,
409+
storyRepository: storyRepository,
410+
pullRequestRepository: pullRequestRepository,
411+
workspaceServiceClient: workspaceServiceClient,
412+
redisRepo: repo,
413+
hashIdGenerator: utils.NewHashIDGenerator(5),
414+
logger: logger.Named("ProjectService"),
415+
asynqClient: asynqClient,
416+
githubIntegrationService: githubIntegrationService,
332417
}
333418
}
334419

app/types/request/create_project_request.go

+8
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,11 @@ type CreateProjectRequest struct {
66
FrontendFramework string `json:"frontend_framework"`
77
Description string `json:"description"`
88
}
9+
10+
type CreateProjectFromGitRequest struct {
11+
Name string `json:"name"`
12+
Framework string `json:"framework"`
13+
FrontendFramework string `json:"frontend_framework"`
14+
Description string `json:"description"`
15+
Repository string `json:"repository"`
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package request
2+
3+
type ImportGitRepository struct {
4+
WorkspaceId string `json:"workspaceId"`
5+
6+
Repository string `json:"repository"`
7+
Username string `json:"username"`
8+
Password string `json:"password"`
9+
10+
RemoteURL string `json:"remoteURL"`
11+
GitnessUser string `json:"gitnessUser"`
12+
GitnessToken string `json:"gitnessToken"`
13+
}

server.go

+2
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ func main() {
479479
projects.POST("", projectsController.CreateProject)
480480
projects.PUT("", projectsController.UpdateProject)
481481

482+
projects.POST("/import", projectsController.CreateProjectFromGit)
483+
482484
projects.GET("/", projectsController.GetAllProjects)
483485
projects.POST("/", projectsController.CreateProject)
484486
projects.PUT("/", projectsController.UpdateProject)

workspace-service/app/controllers/workspace_controller.go

+33
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,39 @@ type WorkspaceController struct {
1212
logger *zap.Logger
1313
}
1414

15+
func (wc *WorkspaceController) ImportExistingWorkspace(c *gin.Context) {
16+
body := dto.ImportGitRepository{}
17+
if err := c.BindJSON(&body); err != nil {
18+
wc.logger.Error("Failed to bind json", zap.Error(err))
19+
c.AbortWithStatusJSON(400, gin.H{
20+
"error": "Bad Request",
21+
})
22+
return
23+
}
24+
25+
wsDetails, err := wc.wsService.ImportGitRepository(
26+
body.WorkspaceId,
27+
body.Repository,
28+
body.Username,
29+
body.Password,
30+
body.RemoteURL,
31+
body.GitnessUser,
32+
body.GitnessToken,
33+
)
34+
35+
if err != nil {
36+
c.AbortWithStatusJSON(
37+
500,
38+
gin.H{"error": "Internal Server Error"},
39+
)
40+
return
41+
}
42+
c.JSON(
43+
200,
44+
gin.H{"message": "success", "workspace": wsDetails},
45+
)
46+
}
47+
1548
func (wc *WorkspaceController) CreateWorkspace(c *gin.Context) {
1649
body := dto.CreateWorkspace{}
1750
if err := c.BindJSON(&body); err != nil {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dto
2+
3+
type ImportGitRepository struct {
4+
WorkspaceId string `json:"workspaceId"`
5+
6+
Repository string `json:"repository"`
7+
Username string `json:"username"`
8+
Password string `json:"password"`
9+
10+
RemoteURL string `json:"remoteURL"`
11+
GitnessUser string `json:"gitnessUser"`
12+
GitnessToken string `json:"gitnessToken"`
13+
}

0 commit comments

Comments
 (0)