Skip to content

Display MySQL user defined error in API Key UI #4590

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

Merged
Merged
Show file tree
Hide file tree
Changes from 2 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
18 changes: 11 additions & 7 deletions pkg/app/server/grpcapi/grpcapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,22 @@ func getEncriptionKey(se *model.Piped_SecretEncryption) ([]byte, error) {
}

func gRPCStoreError(err error, msg string) error {
switch err {
case nil:
if err == nil {
return nil
case datastore.ErrNotFound, filestore.ErrNotFound, stagelogstore.ErrNotFound:
}
if errors.Is(err, datastore.ErrNotFound) || errors.Is(err, filestore.ErrNotFound) || errors.Is(err, stagelogstore.ErrNotFound) {
return status.Error(codes.NotFound, fmt.Sprintf("Entity was not found to %s", msg))
case datastore.ErrInvalidArgument:
}
if errors.Is(err, datastore.ErrInvalidArgument) {
return status.Error(codes.InvalidArgument, fmt.Sprintf("Invalid argument to %s", msg))
case datastore.ErrAlreadyExists:
}
if errors.Is(err, datastore.ErrAlreadyExists) {
return status.Error(codes.AlreadyExists, fmt.Sprintf("Entity already exists to %s", msg))
default:
return status.Error(codes.Internal, fmt.Sprintf("Failed to %s", msg))
}
if errors.Is(err, datastore.ErrUserDefined) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to change from switch case to multiple if 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we generate an error with a message from MySQL, handling errors with a switch cannot work properly. That's why I had to change it to multi-if blocks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which means the switch block can not check the type of datastore.ErrUserDefined error 🤔 It's a bit weird to me. Btw, how about adding test for this function. Looks like this function is testable without mocking anything.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an error message from MySQL is added to datastore.ErrUserDefined, checking with the switch block does not work because the new error is no longer the same as datastore.ErrUserDefined.

I agree with you to add tests for it 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests.

return status.Error(codes.FailedPrecondition, err.Error())
}
return status.Error(codes.Internal, fmt.Sprintf("Failed to %s", msg))
}

func makeUnregisteredAppsCacheKey(projectID string) string {
Expand Down
1 change: 1 addition & 0 deletions pkg/datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var (
ErrInternal = errors.New("internal")
ErrUnimplemented = errors.New("unimplemented")
ErrUnsupported = errors.New("unsupported")
ErrUserDefined = errors.New("user defined error")
)

type Commander string
Expand Down
10 changes: 8 additions & 2 deletions pkg/datastore/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
)

const mysqlErrorCodeDuplicateEntry = 1062
const mysqlErrorCodeUserDefined = 1644

// MySQL client wrapper
type MySQL struct {
Expand Down Expand Up @@ -164,8 +165,13 @@ func (m *MySQL) Create(ctx context.Context, col datastore.Collection, id string,
}

_, err = stmt.ExecContext(ctx, makeRowID(id), data)
if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == mysqlErrorCodeDuplicateEntry {
return datastore.ErrAlreadyExists
if mysqlErr, ok := err.(*mysql.MySQLError); ok {
if mysqlErr.Number == mysqlErrorCodeDuplicateEntry {
return datastore.ErrAlreadyExists
}
if mysqlErr.Number == mysqlErrorCodeUserDefined {
return fmt.Errorf("%w: %s", datastore.ErrUserDefined, mysqlErr.Message)
}
}
if err != nil {
m.logger.Error("failed to create entity",
Expand Down
16 changes: 11 additions & 5 deletions web/src/components/settings-page/api-key/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
DISABLE_API_KEY_SUCCESS,
GENERATE_API_KEY_SUCCESS,
} from "~/constants/toast-text";
import { useAppDispatch, useAppSelector } from "~/hooks/redux";
import { unwrapResult, useAppDispatch, useAppSelector } from "~/hooks/redux";
import {
APIKey,
disableAPIKey,
Expand Down Expand Up @@ -96,10 +96,16 @@ export const APIKeyPage: FC = memo(function APIKeyPage() {

const handleSubmit = useCallback(
(values: { name: string; role: APIKey.Role }) => {
dispatch(generateAPIKey(values)).then(() => {
dispatch(fetchAPIKeys({ enabled: true }));
dispatch(addToast({ message: GENERATE_API_KEY_SUCCESS }));
});
dispatch(generateAPIKey(values))
.then(unwrapResult)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes an existing bug that success toast is displayed even if a request fails.

.then(() => {
console.log("handleSubmit.then");
dispatch(fetchAPIKeys({ enabled: true }));
dispatch(
addToast({ message: GENERATE_API_KEY_SUCCESS, severity: "success" })
);
})
.catch(() => undefined);
},
[dispatch]
);
Expand Down
2 changes: 2 additions & 0 deletions web/src/hooks/redux.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @see https://redux-toolkit.js.org/tutorials/typescript#define-typed-hooks
import { unwrapResult } from "@reduxjs/toolkit";
import {
shallowEqual,
TypedUseSelectorHook,
Expand All @@ -15,3 +16,4 @@ export const useShallowEqualSelector: TypedUseSelectorHook<AppState> = (
) => {
return useSelector(selector, shallowEqual);
};
export { unwrapResult };