Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetProfileByName RPC #4029

Merged
merged 1 commit into from
Jul 29, 2024
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
47 changes: 30 additions & 17 deletions cmd/cli/app/profile/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package profile
import (
"context"
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
Expand All @@ -45,6 +44,7 @@ func getCommand(ctx context.Context, cmd *cobra.Command, _ []string, conn *grpc.
project := viper.GetString("project")
format := viper.GetString("output")
id := viper.GetString("id")
name := viper.GetString("name")

// Ensure the output format is supported
if !app.IsOutputFormatSupported(format) {
Expand All @@ -55,33 +55,50 @@ func getCommand(ctx context.Context, cmd *cobra.Command, _ []string, conn *grpc.
// See https://github.com/spf13/cobra/issues/340#issuecomment-374617413
cmd.SilenceUsage = true

p, err := client.GetProfileById(ctx, &minderv1.GetProfileByIdRequest{
Context: &minderv1.Context{Project: &project},
Id: id,
})
if err != nil {
return cli.MessageAndError("Error getting profile", err)
var prof *minderv1.Profile
if id != "" {
p, err := client.GetProfileById(ctx, &minderv1.GetProfileByIdRequest{
Context: &minderv1.Context{Project: &project},
Id: id,
})
if err != nil {
return cli.MessageAndError("Error getting profile", err)
}

prof = p.GetProfile()
} else if name != "" {
p, err := client.GetProfileByName(ctx, &minderv1.GetProfileByNameRequest{
Context: &minderv1.Context{Project: &project},
Name: name,
})
if err != nil {
return cli.MessageAndError("Error getting profile", err)
}

prof = p.GetProfile()
} else {
return cli.MessageAndError("Error getting profile", fmt.Errorf("id or name required"))
}

switch format {
case app.YAML:
out, err := util.GetYamlFromProto(p)
out, err := util.GetYamlFromProto(prof)
if err != nil {
return cli.MessageAndError("Error getting yaml from proto", err)
}
cmd.Println(out)
case app.JSON:
out, err := util.GetJsonFromProto(p)
out, err := util.GetJsonFromProto(prof)
if err != nil {
return cli.MessageAndError("Error getting json from proto", err)
}
cmd.Println(out)
case app.Table:
settable := NewProfileSettingsTable()
RenderProfileSettingsTable(p.GetProfile(), settable)
RenderProfileSettingsTable(prof, settable)
settable.Render()
table := NewProfileTable()
RenderProfileTable(p.GetProfile(), table)
RenderProfileTable(prof, table)
table.Render()
}
return nil
Expand All @@ -91,12 +108,8 @@ func init() {
ProfileCmd.AddCommand(getCmd)
// Flags
getCmd.Flags().StringP("id", "i", "", "ID for the profile to query")
getCmd.Flags().StringP("name", "n", "", "Name for the profile to query")
getCmd.Flags().StringP("output", "o", app.Table,
fmt.Sprintf("Output format (one of %s)", strings.Join(app.SupportedOutputFormats(), ",")))
// Required
if err := getCmd.MarkFlagRequired("id"); err != nil {
getCmd.Printf("Error marking flag required: %s", err)
os.Exit(1)
}

getCmd.MarkFlagsMutuallyExclusive("id", "name")
}
15 changes: 15 additions & 0 deletions database/mock/store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions database/query/profiles.sql
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,25 @@ JOIN profiles_with_entity_profiles ON profiles.id = profiles_with_entity_profile
LEFT JOIN helper ON profiles.id = helper.profid
WHERE profiles.project_id = $1 AND profiles.id = $2;

-- name: GetProfileByProjectAndName :many
WITH helper AS(
SELECT pr.id as profid,
ARRAY_AGG(ROW(ps.id, ps.profile_id, ps.entity, ps.selector, ps.comment)::profile_selector) AS selectors
FROM profiles pr
JOIN profile_selectors ps
ON pr.id = ps.profile_id
WHERE pr.project_id = $1
GROUP BY pr.id
)
SELECT
sqlc.embed(profiles),
sqlc.embed(profiles_with_entity_profiles),
helper.selectors::profile_selector[] AS profiles_with_selectors
FROM profiles
JOIN profiles_with_entity_profiles ON profiles.id = profiles_with_entity_profiles.profid
LEFT JOIN helper ON profiles.id = helper.profid
WHERE profiles.project_id = $1 AND lower(profiles.name) = lower(sqlc.arg(name));

-- name: GetProfileByID :one
SELECT * FROM profiles WHERE id = $1 AND project_id = $2;

Expand Down
24 changes: 24 additions & 0 deletions docs/docs/ref/proto.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions internal/controlplane/handlers_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,52 @@ func (s *Server) GetProfileById(ctx context.Context,
}, nil
}

// GetProfileByName implements the RPC method for getting a profile by name
func (s *Server) GetProfileByName(ctx context.Context,
in *minderv1.GetProfileByNameRequest) (*minderv1.GetProfileByNameResponse, error) {
entityCtx := engcontext.EntityFromContext(ctx)

err := entityCtx.ValidateProject(ctx, s.store)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "error in entity context: %v", err)
}

if in.Name == "" {
return nil, util.UserVisibleError(codes.InvalidArgument, "profile name must be specified")
}

profiles, err := s.store.GetProfileByProjectAndName(ctx, db.GetProfileByProjectAndNameParams{
ProjectID: entityCtx.Project.ID,
Name: in.Name,
})
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, util.UserVisibleError(codes.NotFound, "profile %q not found", in.Name)
}
return nil, err
}

pols := prof.MergeDatabaseGetByNameIntoProfiles(profiles)

// Telemetry logging
logger.BusinessRecord(ctx).Project = entityCtx.Project.ID

if len(pols) == 0 {
return nil, util.UserVisibleError(codes.NotFound, "profile %q not found", in.Name)
} else if len(pols) > 1 {
return nil, fmt.Errorf("expected only one profile, got %d", len(pols))
}

// This should be only one profile
for _, profile := range pols {
return &minderv1.GetProfileByNameResponse{
Profile: profile,
}, nil
}

return nil, util.UserVisibleError(codes.NotFound, "profile %q not found", in.Name)
}

func getProfilePBFromDB(
ctx context.Context,
id uuid.UUID,
Expand Down
75 changes: 75 additions & 0 deletions internal/db/profiles.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/db/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading