mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
feat: Add "projects list" command to the CLI (#333)
This adds a WorkspaceOwnerCount parameter returned from the projects API. It's helpful to display the amount of usage a specific project has.
This commit is contained in:
@ -67,7 +67,7 @@ func login() *cobra.Command {
|
|||||||
if !isTTY(cmd) {
|
if !isTTY(cmd) {
|
||||||
return xerrors.New("the initial user cannot be created in non-interactive mode. use the API")
|
return xerrors.New("the initial user cannot be created in non-interactive mode. use the API")
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", color.HiBlackString(">"))
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Your Coder deployment hasn't been set up!\n", caret)
|
||||||
|
|
||||||
_, err := prompt(cmd, &promptui.Prompt{
|
_, err := prompt(cmd, &promptui.Prompt{
|
||||||
Label: "Would you like to create the first user?",
|
Label: "Would you like to create the first user?",
|
||||||
@ -147,7 +147,7 @@ func login() *cobra.Command {
|
|||||||
return xerrors.Errorf("write server url: %w", err)
|
return xerrors.Errorf("write server url: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", color.HiBlackString(">"), color.HiCyanString(username))
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", caret, color.HiCyanString(username))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ func login() *cobra.Command {
|
|||||||
return xerrors.Errorf("write server url: %w", err)
|
return xerrors.Errorf("write server url: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", color.HiBlackString(">"), color.HiCyanString(resp.Username))
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Welcome to Coder, %s! You're authenticated.\n", caret, color.HiCyanString(resp.Username))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ func projectCreate() *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s The %s project has been created!\n", color.HiBlackString(">"), color.HiCyanString(project.Name))
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s The %s project has been created!\n", caret, color.HiCyanString(project.Name))
|
||||||
_, err = prompt(cmd, &promptui.Prompt{
|
_, err = prompt(cmd, &promptui.Prompt{
|
||||||
Label: "Create a new workspace?",
|
Label: "Create a new workspace?",
|
||||||
IsConfirm: true,
|
IsConfirm: true,
|
||||||
|
63
cli/projectlist.go
Normal file
63
cli/projectlist.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"text/tabwriter"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func projectList() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
client, err := createClient(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
start := time.Now()
|
||||||
|
organization, err := currentOrganization(cmd, client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
projects, err := client.Projects(cmd.Context(), organization.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(projects) == 0 {
|
||||||
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s No projects found in %s! Create one:\n\n", caret, color.HiWhiteString(organization.Name))
|
||||||
|
_, _ = fmt.Fprintln(cmd.OutOrStdout(), color.HiMagentaString(" $ coder projects create <directory>\n"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Projects found in %s %s\n\n",
|
||||||
|
caret,
|
||||||
|
color.HiWhiteString(organization.Name),
|
||||||
|
color.HiBlackString("[%dms]",
|
||||||
|
time.Since(start).Milliseconds()))
|
||||||
|
|
||||||
|
writer := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 4, ' ', 0)
|
||||||
|
_, _ = fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n",
|
||||||
|
color.HiBlackString("Project"),
|
||||||
|
color.HiBlackString("Source"),
|
||||||
|
color.HiBlackString("Last Updated"),
|
||||||
|
color.HiBlackString("Used By"))
|
||||||
|
for _, project := range projects {
|
||||||
|
suffix := ""
|
||||||
|
if project.WorkspaceOwnerCount != 1 {
|
||||||
|
suffix = "s"
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n",
|
||||||
|
color.New(color.FgHiCyan).Sprint(project.Name),
|
||||||
|
color.WhiteString("Archive"),
|
||||||
|
color.WhiteString(project.UpdatedAt.Format("January 2, 2006")),
|
||||||
|
color.New(color.FgHiWhite).Sprintf("%d developer%s", project.WorkspaceOwnerCount, suffix))
|
||||||
|
}
|
||||||
|
return writer.Flush()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
56
cli/projectlist_test.go
Normal file
56
cli/projectlist_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package cli_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/coder/coder/cli/clitest"
|
||||||
|
"github.com/coder/coder/coderd/coderdtest"
|
||||||
|
"github.com/coder/coder/pty/ptytest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProjectList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
t.Run("None", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t)
|
||||||
|
coderdtest.CreateInitialUser(t, client)
|
||||||
|
cmd, root := clitest.New(t, "projects", "list")
|
||||||
|
clitest.SetupConfig(t, client, root)
|
||||||
|
pty := ptytest.New(t)
|
||||||
|
cmd.SetIn(pty.Input())
|
||||||
|
cmd.SetOut(pty.Output())
|
||||||
|
closeChan := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.NoError(t, err)
|
||||||
|
close(closeChan)
|
||||||
|
}()
|
||||||
|
pty.ExpectMatch("No projects found")
|
||||||
|
<-closeChan
|
||||||
|
})
|
||||||
|
t.Run("List", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t)
|
||||||
|
user := coderdtest.CreateInitialUser(t, client)
|
||||||
|
daemon := coderdtest.NewProvisionerDaemon(t, client)
|
||||||
|
job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil)
|
||||||
|
coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID)
|
||||||
|
_ = daemon.Close()
|
||||||
|
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||||
|
cmd, root := clitest.New(t, "projects", "list")
|
||||||
|
clitest.SetupConfig(t, client, root)
|
||||||
|
pty := ptytest.New(t)
|
||||||
|
cmd.SetIn(pty.Input())
|
||||||
|
cmd.SetOut(pty.Output())
|
||||||
|
closeChan := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
err := cmd.Execute()
|
||||||
|
require.NoError(t, err)
|
||||||
|
close(closeChan)
|
||||||
|
}()
|
||||||
|
pty.ExpectMatch(project.Name)
|
||||||
|
<-closeChan
|
||||||
|
})
|
||||||
|
}
|
@ -30,9 +30,12 @@ func projects() *cobra.Command {
|
|||||||
|
|
||||||
` + color.New(color.FgHiMagenta).Sprint("$ coder projects update <name>"),
|
` + color.New(color.FgHiMagenta).Sprint("$ coder projects update <name>"),
|
||||||
}
|
}
|
||||||
cmd.AddCommand(projectCreate())
|
cmd.AddCommand(
|
||||||
cmd.AddCommand(projectPlan())
|
projectCreate(),
|
||||||
cmd.AddCommand(projectUpdate())
|
projectList(),
|
||||||
|
projectPlan(),
|
||||||
|
projectUpdate(),
|
||||||
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,10 @@ import (
|
|||||||
"github.com/coder/coder/codersdk"
|
"github.com/coder/coder/codersdk"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
caret = color.HiBlackString(">")
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
varGlobalConfig = "global-config"
|
varGlobalConfig = "global-config"
|
||||||
varNoOpen = "no-open"
|
varNoOpen = "no-open"
|
||||||
|
@ -54,7 +54,7 @@ func workspaceCreate() *cobra.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Previewing project create...\n", color.HiBlackString(">"))
|
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s Previewing project create...\n", caret)
|
||||||
|
|
||||||
project, err := client.Project(cmd.Context(), organization.Name, args[0])
|
project, err := client.Project(cmd.Context(), organization.Name, args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -30,7 +31,16 @@ type CreateParameterValueRequest struct {
|
|||||||
// Project is the JSON representation of a Coder project.
|
// Project is the JSON representation of a Coder project.
|
||||||
// This type matches the database object for now, but is
|
// This type matches the database object for now, but is
|
||||||
// abstracted for ease of change later on.
|
// abstracted for ease of change later on.
|
||||||
type Project database.Project
|
type Project struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
OrganizationID string `json:"organization_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Provisioner database.ProvisionerType `json:"provisioner"`
|
||||||
|
ActiveVersionID uuid.UUID `json:"active_version_id"`
|
||||||
|
WorkspaceOwnerCount uint32 `json:"workspace_owner_count"`
|
||||||
|
}
|
||||||
|
|
||||||
// CreateProjectRequest enables callers to create a new Project.
|
// CreateProjectRequest enables callers to create a new Project.
|
||||||
type CreateProjectRequest struct {
|
type CreateProjectRequest struct {
|
||||||
@ -69,11 +79,22 @@ func (api *api) projects(rw http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if projects == nil {
|
projectIDs := make([]uuid.UUID, 0, len(projects))
|
||||||
projects = []database.Project{}
|
for _, project := range projects {
|
||||||
|
projectIDs = append(projectIDs, project.ID)
|
||||||
|
}
|
||||||
|
workspaceCounts, err := api.Database.GetWorkspaceOwnerCountsByProjectIDs(r.Context(), projectIDs)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||||
|
Message: fmt.Sprintf("get workspace counts: %s", err.Error()),
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
render.Status(r, http.StatusOK)
|
render.Status(r, http.StatusOK)
|
||||||
render.JSON(rw, r, projects)
|
render.JSON(rw, r, convertProjects(projects, workspaceCounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lists all projects in an organization.
|
// Lists all projects in an organization.
|
||||||
@ -89,11 +110,22 @@ func (api *api) projectsByOrganization(rw http.ResponseWriter, r *http.Request)
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if projects == nil {
|
projectIDs := make([]uuid.UUID, 0, len(projects))
|
||||||
projects = []database.Project{}
|
for _, project := range projects {
|
||||||
|
projectIDs = append(projectIDs, project.ID)
|
||||||
|
}
|
||||||
|
workspaceCounts, err := api.Database.GetWorkspaceOwnerCountsByProjectIDs(r.Context(), projectIDs)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||||
|
Message: fmt.Sprintf("get workspace counts: %s", err.Error()),
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
render.Status(r, http.StatusOK)
|
render.Status(r, http.StatusOK)
|
||||||
render.JSON(rw, r, projects)
|
render.JSON(rw, r, convertProjects(projects, workspaceCounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new project in an organization.
|
// Create a new project in an organization.
|
||||||
@ -162,7 +194,7 @@ func (api *api) postProjectsByOrganization(rw http.ResponseWriter, r *http.Reque
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("insert project version: %s", err)
|
return xerrors.Errorf("insert project version: %s", err)
|
||||||
}
|
}
|
||||||
project = Project(dbProject)
|
project = convertProject(dbProject, 0)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -241,6 +273,38 @@ func (api *api) parametersByProject(rw http.ResponseWriter, r *http.Request) {
|
|||||||
render.JSON(rw, r, apiParameterValues)
|
render.JSON(rw, r, apiParameterValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertProjects(projects []database.Project, workspaceCounts []database.GetWorkspaceOwnerCountsByProjectIDsRow) []Project {
|
||||||
|
apiProjects := make([]Project, 0, len(projects))
|
||||||
|
for _, project := range projects {
|
||||||
|
found := false
|
||||||
|
for _, workspaceCount := range workspaceCounts {
|
||||||
|
if workspaceCount.ProjectID.String() != project.ID.String() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
apiProjects = append(apiProjects, convertProject(project, uint32(workspaceCount.Count)))
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
apiProjects = append(apiProjects, convertProject(project, uint32(0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apiProjects
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertProject(project database.Project, workspaceOwnerCount uint32) Project {
|
||||||
|
return Project{
|
||||||
|
ID: project.ID,
|
||||||
|
CreatedAt: project.CreatedAt,
|
||||||
|
UpdatedAt: project.UpdatedAt,
|
||||||
|
OrganizationID: project.OrganizationID,
|
||||||
|
Name: project.Name,
|
||||||
|
Provisioner: project.Provisioner,
|
||||||
|
ActiveVersionID: project.ActiveVersionID,
|
||||||
|
WorkspaceOwnerCount: workspaceOwnerCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func convertParameterValue(parameterValue database.ParameterValue) ParameterValue {
|
func convertParameterValue(parameterValue database.ParameterValue) ParameterValue {
|
||||||
parameterValue.SourceValue = ""
|
parameterValue.SourceValue = ""
|
||||||
return ParameterValue(parameterValue)
|
return ParameterValue(parameterValue)
|
||||||
|
@ -36,6 +36,22 @@ func TestProjects(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, projects, 1)
|
require.Len(t, projects, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("ListWorkspaceOwnerCount", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
client := coderdtest.New(t)
|
||||||
|
user := coderdtest.CreateInitialUser(t, client)
|
||||||
|
coderdtest.NewProvisionerDaemon(t, client)
|
||||||
|
job := coderdtest.CreateProjectImportJob(t, client, user.Organization, nil)
|
||||||
|
coderdtest.AwaitProjectImportJob(t, client, user.Organization, job.ID)
|
||||||
|
project := coderdtest.CreateProject(t, client, user.Organization, job.ID)
|
||||||
|
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||||
|
_ = coderdtest.CreateWorkspace(t, client, "", project.ID)
|
||||||
|
projects, err := client.Projects(context.Background(), "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, projects, 1)
|
||||||
|
require.Equal(t, projects[0].WorkspaceOwnerCount, uint32(1))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProjectsByOrganization(t *testing.T) {
|
func TestProjectsByOrganization(t *testing.T) {
|
||||||
|
@ -195,6 +195,41 @@ func (q *fakeQuerier) GetWorkspaceByUserIDAndName(_ context.Context, arg databas
|
|||||||
return database.Workspace{}, sql.ErrNoRows
|
return database.Workspace{}, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *fakeQuerier) GetWorkspaceOwnerCountsByProjectIDs(_ context.Context, projectIDs []uuid.UUID) ([]database.GetWorkspaceOwnerCountsByProjectIDsRow, error) {
|
||||||
|
counts := map[string]map[string]struct{}{}
|
||||||
|
for _, projectID := range projectIDs {
|
||||||
|
found := false
|
||||||
|
for _, workspace := range q.workspace {
|
||||||
|
if workspace.ProjectID.String() != projectID.String() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
countByOwnerID, ok := counts[projectID.String()]
|
||||||
|
if !ok {
|
||||||
|
countByOwnerID = map[string]struct{}{}
|
||||||
|
}
|
||||||
|
countByOwnerID[workspace.OwnerID] = struct{}{}
|
||||||
|
counts[projectID.String()] = countByOwnerID
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
counts[projectID.String()] = map[string]struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res := make([]database.GetWorkspaceOwnerCountsByProjectIDsRow, 0)
|
||||||
|
for key, value := range counts {
|
||||||
|
uid := uuid.MustParse(key)
|
||||||
|
res = append(res, database.GetWorkspaceOwnerCountsByProjectIDsRow{
|
||||||
|
ProjectID: uid,
|
||||||
|
Count: int64(len(value)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(res) == 0 {
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (q *fakeQuerier) GetWorkspaceResourcesByHistoryID(_ context.Context, workspaceHistoryID uuid.UUID) ([]database.WorkspaceResource, error) {
|
func (q *fakeQuerier) GetWorkspaceResourcesByHistoryID(_ context.Context, workspaceHistoryID uuid.UUID) ([]database.WorkspaceResource, error) {
|
||||||
q.mutex.Lock()
|
q.mutex.Lock()
|
||||||
defer q.mutex.Unlock()
|
defer q.mutex.Unlock()
|
||||||
|
@ -39,6 +39,7 @@ type querier interface {
|
|||||||
GetWorkspaceHistoryByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceHistory, error)
|
GetWorkspaceHistoryByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceHistory, error)
|
||||||
GetWorkspaceHistoryByWorkspaceIDAndName(ctx context.Context, arg GetWorkspaceHistoryByWorkspaceIDAndNameParams) (WorkspaceHistory, error)
|
GetWorkspaceHistoryByWorkspaceIDAndName(ctx context.Context, arg GetWorkspaceHistoryByWorkspaceIDAndNameParams) (WorkspaceHistory, error)
|
||||||
GetWorkspaceHistoryByWorkspaceIDWithoutAfter(ctx context.Context, workspaceID uuid.UUID) (WorkspaceHistory, error)
|
GetWorkspaceHistoryByWorkspaceIDWithoutAfter(ctx context.Context, workspaceID uuid.UUID) (WorkspaceHistory, error)
|
||||||
|
GetWorkspaceOwnerCountsByProjectIDs(ctx context.Context, ids []uuid.UUID) ([]GetWorkspaceOwnerCountsByProjectIDsRow, error)
|
||||||
GetWorkspaceResourcesByHistoryID(ctx context.Context, workspaceHistoryID uuid.UUID) ([]WorkspaceResource, error)
|
GetWorkspaceResourcesByHistoryID(ctx context.Context, workspaceHistoryID uuid.UUID) ([]WorkspaceResource, error)
|
||||||
GetWorkspacesByProjectAndUserID(ctx context.Context, arg GetWorkspacesByProjectAndUserIDParams) ([]Workspace, error)
|
GetWorkspacesByProjectAndUserID(ctx context.Context, arg GetWorkspacesByProjectAndUserIDParams) ([]Workspace, error)
|
||||||
GetWorkspacesByUserID(ctx context.Context, ownerID string) ([]Workspace, error)
|
GetWorkspacesByUserID(ctx context.Context, ownerID string) ([]Workspace, error)
|
||||||
|
@ -278,6 +278,18 @@ WHERE
|
|||||||
owner_id = $1
|
owner_id = $1
|
||||||
AND project_id = $2;
|
AND project_id = $2;
|
||||||
|
|
||||||
|
-- name: GetWorkspaceOwnerCountsByProjectIDs :many
|
||||||
|
SELECT
|
||||||
|
project_id,
|
||||||
|
COUNT(DISTINCT owner_id)
|
||||||
|
FROM
|
||||||
|
workspace
|
||||||
|
WHERE
|
||||||
|
project_id = ANY(@ids :: uuid [ ])
|
||||||
|
GROUP BY
|
||||||
|
project_id,
|
||||||
|
owner_id;
|
||||||
|
|
||||||
-- name: GetWorkspaceHistoryByID :one
|
-- name: GetWorkspaceHistoryByID :one
|
||||||
SELECT
|
SELECT
|
||||||
*
|
*
|
||||||
|
@ -1070,6 +1070,47 @@ func (q *sqlQuerier) GetWorkspaceHistoryByWorkspaceIDWithoutAfter(ctx context.Co
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getWorkspaceOwnerCountsByProjectIDs = `-- name: GetWorkspaceOwnerCountsByProjectIDs :many
|
||||||
|
SELECT
|
||||||
|
project_id,
|
||||||
|
COUNT(DISTINCT owner_id)
|
||||||
|
FROM
|
||||||
|
workspace
|
||||||
|
WHERE
|
||||||
|
project_id = ANY($1 :: uuid [ ])
|
||||||
|
GROUP BY
|
||||||
|
project_id,
|
||||||
|
owner_id
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWorkspaceOwnerCountsByProjectIDsRow struct {
|
||||||
|
ProjectID uuid.UUID `db:"project_id" json:"project_id"`
|
||||||
|
Count int64 `db:"count" json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *sqlQuerier) GetWorkspaceOwnerCountsByProjectIDs(ctx context.Context, ids []uuid.UUID) ([]GetWorkspaceOwnerCountsByProjectIDsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getWorkspaceOwnerCountsByProjectIDs, pq.Array(ids))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetWorkspaceOwnerCountsByProjectIDsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetWorkspaceOwnerCountsByProjectIDsRow
|
||||||
|
if err := rows.Scan(&i.ProjectID, &i.Count); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getWorkspaceResourcesByHistoryID = `-- name: GetWorkspaceResourcesByHistoryID :many
|
const getWorkspaceResourcesByHistoryID = `-- name: GetWorkspaceResourcesByHistoryID :many
|
||||||
SELECT
|
SELECT
|
||||||
id, created_at, workspace_history_id, type, name, workspace_agent_token, workspace_agent_id
|
id, created_at, workspace_history_id, type, name, workspace_agent_token, workspace_agent_id
|
||||||
|
Reference in New Issue
Block a user