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

Display MySQL user defined error in API Key UI #4590

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 gRPCStoreError(err error, msg string) error {
switch err {
case nil:
if err == nil {

Check warning on line 169 in pkg/app/server/grpcapi/grpcapi.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/server/grpcapi/grpcapi.go#L169

Added line #L169 was not covered by tests
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) {

Check warning on line 172 in pkg/app/server/grpcapi/grpcapi.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/server/grpcapi/grpcapi.go#L171-L172

Added lines #L171 - L172 were not covered by tests
return status.Error(codes.NotFound, fmt.Sprintf("Entity was not found to %s", msg))
case datastore.ErrInvalidArgument:
}
if errors.Is(err, datastore.ErrInvalidArgument) {

Check warning on line 175 in pkg/app/server/grpcapi/grpcapi.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/server/grpcapi/grpcapi.go#L174-L175

Added lines #L174 - L175 were not covered by tests
return status.Error(codes.InvalidArgument, fmt.Sprintf("Invalid argument to %s", msg))
case datastore.ErrAlreadyExists:
}
if errors.Is(err, datastore.ErrAlreadyExists) {

Check warning on line 178 in pkg/app/server/grpcapi/grpcapi.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/server/grpcapi/grpcapi.go#L177-L178

Added lines #L177 - L178 were not covered by tests
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))

Check warning on line 184 in pkg/app/server/grpcapi/grpcapi.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/server/grpcapi/grpcapi.go#L181-L184

Added lines #L181 - L184 were not covered by tests
}

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 @@
)

const mysqlErrorCodeDuplicateEntry = 1062
const mysqlErrorCodeUserDefined = 1644

// MySQL client wrapper
type MySQL struct {
Expand Down Expand Up @@ -164,8 +165,13 @@
}

_, 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)
}

Check warning on line 174 in pkg/datastore/mysql/mysql.go

View check run for this annotation

Codecov / codecov/patch

pkg/datastore/mysql/mysql.go#L168-L174

Added lines #L168 - L174 were not covered by tests
}
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 @@
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 @@

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(() => {});

Check failure on line 108 in web/src/components/settings-page/api-key/index.tsx

View workflow job for this annotation

GitHub Actions / web

Unexpected empty arrow function
},
[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 };
Loading