Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion app/cli/cmd/organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func newOrganizationCmd() *cobra.Command {
Short: "Organizations management",
}

cmd.AddCommand(newOrganizationList(), newOrganizationSet(), newOrganizationDescribeCmd())
cmd.AddCommand(
newOrganizationList(),
newOrganizationSet(),
newOrganizationDescribeCmd(),
newOrganizationInvitationCmd(),
)
return cmd
}
2 changes: 1 addition & 1 deletion app/cli/cmd/organization_describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func contextTableOutput(config *action.ConfigContextItem) error {
gt.SetTitle("Current Context")
gt.AppendRow(table.Row{"Logged in as", config.CurrentUser.Email})
gt.AppendSeparator()
gt.AppendRow(table.Row{"Organization", config.CurrentOrg.Name})
gt.AppendRow(table.Row{"Organization", fmt.Sprintf("%s (%s)", config.CurrentOrg.Name, config.CurrentOrg.ID)})
backend := config.CurrentCASBackend
if backend != nil {
gt.AppendSeparator()
Expand Down
32 changes: 32 additions & 0 deletions app/cli/cmd/organization_invitation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright 2023 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"github.com/spf13/cobra"
)

func newOrganizationInvitationCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "invitation",
Aliases: []string{"invite"},
Short: "User Invitations",
}

cmd.AddCommand(newOrganizationInvitationListSentCmd(), newOrganizationInvitationCreateCmd(), newOrganizationInvitationRevokeCmd())

return cmd
}
50 changes: 50 additions & 0 deletions app/cli/cmd/organization_invitation_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Copyright 2023 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"

"github.com/chainloop-dev/chainloop/app/cli/internal/action"
"github.com/spf13/cobra"
)

func newOrganizationInvitationCreateCmd() *cobra.Command {
var receiverEmail, organizationID string
cmd := &cobra.Command{
Use: "create",
Short: "Invite a User to an Organization",
RunE: func(cmd *cobra.Command, args []string) error {
res, err := action.NewOrgInvitationCreate(actionOpts).Run(
context.Background(), organizationID, receiverEmail)
if err != nil {
return err
}

return encodeOutput([]*action.OrgInvitationItem{res}, orgInvitationTableOutput)
},
}

cmd.Flags().StringVar(&receiverEmail, "receiver", "", "Email of the user to invite")
err := cmd.MarkFlagRequired("receiver")
cobra.CheckErr(err)

cmd.Flags().StringVar(&organizationID, "organization", "", "ID of the organization to invite the user to")
err = cmd.MarkFlagRequired("organization")
cobra.CheckErr(err)

return cmd
}
62 changes: 62 additions & 0 deletions app/cli/cmd/organization_invitation_list_sent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Copyright 2023 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"
"fmt"
"time"

"github.com/chainloop-dev/chainloop/app/cli/internal/action"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
)

func newOrganizationInvitationListSentCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List sent invitations",
RunE: func(cmd *cobra.Command, args []string) error {
res, err := action.NewOrgInvitationListSent(actionOpts).Run(context.Background())
if err != nil {
return err
}

return encodeOutput(res, orgInvitationTableOutput)
},
}

return cmd
}

func orgInvitationTableOutput(items []*action.OrgInvitationItem) error {
if len(items) == 0 {
fmt.Println("there are no sent invitations")
return nil
}

t := newTableWriter()
t.AppendHeader(table.Row{"ID", "Org Name", "Receiver Email", "Status", "Created At"})

for _, i := range items {
t.AppendRow(table.Row{i.ID, i.Organization.Name, i.ReceiverEmail, i.Status, i.CreatedAt.Format(time.RFC822)})
t.AppendSeparator()
}

t.Render()
return nil
}
45 changes: 45 additions & 0 deletions app/cli/cmd/organization_invitation_revoke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Copyright 2023 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"context"

"github.com/chainloop-dev/chainloop/app/cli/internal/action"
"github.com/spf13/cobra"
)

func newOrganizationInvitationRevokeCmd() *cobra.Command {
var invitationID string
cmd := &cobra.Command{
Use: "revoke",
Short: "Revoke a pending invitation",
RunE: func(cmd *cobra.Command, args []string) error {
if err := action.NewOrgInvitationRevoke(actionOpts).Run(context.Background(), invitationID); err != nil {
return err
}

logger.Info().Msg("Invitation Revoked!")
return nil
},
}

cmd.Flags().StringVar(&invitationID, "id", "", "Invitation ID")
err := cmd.MarkFlagRequired("id")
cobra.CheckErr(err)

return cmd
}
4 changes: 2 additions & 2 deletions app/cli/cmd/organization_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ func orgMembershipTableOutput(items []*action.MembershipItem) error {
}

t := newTableWriter()
t.AppendHeader(table.Row{"ID", "Org Name", "Current", "Joined At"})
t.AppendHeader(table.Row{"Org ID", "Org Name", "Current", "Joined At"})

for _, i := range items {
t.AppendRow(table.Row{i.ID, i.Org.Name, i.Current, i.CreatedAt.Format(time.RFC822)})
t.AppendRow(table.Row{i.Org.ID, i.Org.Name, i.Current, i.CreatedAt.Format(time.RFC822)})
t.AppendSeparator()
}

Expand Down
28 changes: 24 additions & 4 deletions app/cli/cmd/organization_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,48 @@
package cmd

import (
"fmt"

"github.com/chainloop-dev/chainloop/app/cli/internal/action"
"github.com/spf13/cobra"
)

func newOrganizationSet() *cobra.Command {
var membershipID string
var orgID string

cmd := &cobra.Command{
Use: "set",
Short: "Set the current organization associated with this user",
RunE: func(cmd *cobra.Command, args []string) error {
res, err := action.NewMembershipSet(actionOpts).Run(membershipID)
// To change the current organization, we need to find the membership ID
memberships, err := action.NewMembershipList(actionOpts).Run()
if err != nil {
return err
}

var membershipID string
for _, m := range memberships {
if m.Org.ID == orgID {
membershipID = m.ID
break
}
}

if membershipID == "" {
return fmt.Errorf("organization %s not found", orgID)
}

m, err := action.NewMembershipSet(actionOpts).Run(membershipID)
if err != nil {
return err
}

logger.Info().Msg("Organization switched!")
return encodeOutput([]*action.MembershipItem{res}, orgMembershipTableOutput)
return encodeOutput([]*action.MembershipItem{m}, orgMembershipTableOutput)
},
}

cmd.Flags().StringVar(&membershipID, "id", "", "membership ID to make the switch")
cmd.Flags().StringVar(&orgID, "id", "", "organization ID to make the switch")
cobra.CheckErr(cmd.MarkFlagRequired("id"))

return cmd
Expand Down
3 changes: 2 additions & 1 deletion app/cli/cmd/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type tabulatedData interface {
[]*action.AvailableIntegrationItem |
[]*action.AttachedIntegrationItem |
[]*action.MembershipItem |
[]*action.CASBackendItem
[]*action.CASBackendItem |
[]*action.OrgInvitationItem
}

var ErrOutputFormatNotImplemented = errors.New("format not implemented")
Expand Down
22 changes: 15 additions & 7 deletions app/cli/internal/action/config_current_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ func NewConfigCurrentContext(cfg *ActionsOpts) *ConfigCurrentContext {
}

type ConfigContextItem struct {
CurrentUser *ConfigContextItemUser
CurrentUser *UserItem
CurrentOrg *OrgItem
CurrentCASBackend *CASBackendItem
}

type ConfigContextItemUser struct {
type UserItem struct {
ID, Email string
CreatedAt *time.Time
}
Expand All @@ -51,12 +51,20 @@ func (action *ConfigCurrentContext) Run() (*ConfigContextItem, error) {
res := resp.GetResult()

return &ConfigContextItem{
CurrentUser: &ConfigContextItemUser{
ID: res.GetCurrentUser().Id,
Email: res.GetCurrentUser().Email,
CreatedAt: toTimePtr(res.GetCurrentUser().CreatedAt.AsTime()),
},
CurrentUser: pbUserItemToAction(res.GetCurrentUser()),
CurrentOrg: pbOrgItemToAction(res.GetCurrentOrg()),
CurrentCASBackend: pbCASBackendItemToAction(res.GetCurrentCasBackend()),
}, nil
}

func pbUserItemToAction(in *pb.User) *UserItem {
if in == nil {
return nil
}

return &UserItem{
ID: in.Id,
Email: in.Email,
CreatedAt: toTimePtr(in.CreatedAt.AsTime()),
}
}
44 changes: 44 additions & 0 deletions app/cli/internal/action/org_invitation_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// Copyright 2023 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package action

import (
"context"

pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1"
)

type OrgInvitationCreate struct {
cfg *ActionsOpts
}

func NewOrgInvitationCreate(cfg *ActionsOpts) *OrgInvitationCreate {
return &OrgInvitationCreate{cfg}
}

func (action *OrgInvitationCreate) Run(ctx context.Context, organization, receiver string) (*OrgInvitationItem, error) {
client := pb.NewOrgInvitationServiceClient(action.cfg.CPConnection)
resp, err := client.Create(ctx, &pb.OrgInvitationServiceCreateRequest{
OrganizationId: organization,
ReceiverEmail: receiver,
})

if err != nil {
return nil, err
}

return pbOrgInvitationItemToAction(resp.Result), nil
}
Loading