diff --git a/internal/controlplane/handlers_oauth.go b/internal/controlplane/handlers_oauth.go index 083e8c63cd..3dcedbaeae 100644 --- a/internal/controlplane/handlers_oauth.go +++ b/internal/controlplane/handlers_oauth.go @@ -418,7 +418,9 @@ func (s *Server) StoreProviderToken(ctx context.Context, _, err = s.store.CreateAccessToken(ctx, db.CreateAccessTokenParams{ProjectID: projectID, Provider: provider.Name, EncryptedToken: encodedToken, OwnerFilter: owner}) - if err != nil { + if db.ErrIsUniqueViolation(err) { + return nil, util.UserVisibleError(codes.AlreadyExists, "token already exists") + } else if err != nil { return nil, status.Errorf(codes.Internal, "error storing access token: %v", err) } return &pb.StoreProviderTokenResponse{}, nil diff --git a/internal/controlplane/handlers_profile.go b/internal/controlplane/handlers_profile.go index cc00ff3d57..d1cd43fbc9 100644 --- a/internal/controlplane/handlers_profile.go +++ b/internal/controlplane/handlers_profile.go @@ -212,7 +212,10 @@ func (s *Server) CreateProfile(ctx context.Context, // Create profile profile, err := qtx.CreateProfile(ctx, params) - if err != nil { + if db.ErrIsUniqueViolation(err) { + log.Printf("profile already exists: %v", err) + return nil, util.UserVisibleError(codes.AlreadyExists, "profile already exists") + } else if err != nil { log.Printf("error creating profile: %v", err) return nil, status.Errorf(codes.Internal, "error creating profile") } diff --git a/internal/db/errors.go b/internal/db/errors.go new file mode 100644 index 0000000000..9cbc69d50b --- /dev/null +++ b/internal/db/errors.go @@ -0,0 +1,34 @@ +// Copyright 2023 Stacklok, Inc. +// +// 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 db + +import ( + "errors" + + "github.com/lib/pq" +) + +// ErrIsUniqueViolation returns true if the error is a unique violation +func ErrIsUniqueViolation(err error) bool { + return isPostgresError(err, "23505") +} + +func isPostgresError(err error, code string) bool { + var pgErr *pq.Error + if errors.As(err, &pgErr) { + return pgErr.Code == pq.ErrorCode(code) + } + return false +}