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

feat: add a more precise error handling to the SDK #36

Merged
merged 11 commits into from
Dec 8, 2023

Conversation

mojtaba-esk
Copy link
Member

API endpoints return the errors in MetaMessage format which makes it easier for the end user to match and handle errors.

This PR attempts to add such functionality to the SDK functions; moreover, it adds some helper functions for the users to match errors easier.

@mojtaba-esk mojtaba-esk added the enhancement New feature or request label Dec 6, 2023
@mojtaba-esk mojtaba-esk requested a review from a team December 6, 2023 15:29
@mojtaba-esk mojtaba-esk self-assigned this Dec 6, 2023
Copy link
Member

@smuu smuu left a comment

Choose a reason for hiding this comment

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

Thanks for this!
I have two small questions.

sdk/client.go Outdated
if err := json.Unmarshal(resp, &msg); err != nil {
// if the response is not a MetaMessage, it's probably a success message
// Therefore we just return the original error from the request
return resErr
Copy link
Member

Choose a reason for hiding this comment

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

Why is the success message in the response error?

Copy link
Member Author

Choose a reason for hiding this comment

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

The main reason is sometimes the response message is not in the MetaMessage format therefore the unmarshal fails and there might be simply an http error like could not find the server, etc.. so we return the response error in that case.
I think it need to have more clear comment.
or maybe we change it to a better way. do you have any suggestions in mind?

Copy link
Member Author

Choose a reason for hiding this comment

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

pushed a new change, hope the new one is more clear. let me know plz

Copy link
Member

Choose a reason for hiding this comment

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

I got it now. Thank you for clarifying 👍

// Therefore we just return the original error from the request
return resErr
}
return Error{Message: msg}
Copy link
Member

Choose a reason for hiding this comment

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

It's currently not clear to me when it succeeds and when it fails.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well the HTTP request can succeed, but the requested action might fail. for example when user requests to start a packetloss on a non existing network interface. The HTTP request to the endpoint succeeds, but the underlying service returns an error in form of MetaMessage with details.
So there are two layers of errors here.
Not sure how to make it clearer.

Copy link
Member Author

Choose a reason for hiding this comment

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

pushed a proposal fix

Copy link
Member

Choose a reason for hiding this comment

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

The new comment explains it better now. Also, the new structure looks good 👍

@mojtaba-esk mojtaba-esk requested a review from a team December 7, 2023 09:11
@mojtaba-esk mojtaba-esk requested a review from smuu December 7, 2023 10:04
}

func (c *Client) PacketlossStatus() (*MetaMessage, error) {
return c.getServiceStatus("/packetloss/status")
return c.getServiceStatus(endpointPrefix + "/packetloss/status")
Copy link

Choose a reason for hiding this comment

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

i think it'd be nice to do something like api.BuildEndpoint("/packetloss/status") for things like this, and lose the endpointPrefix, then you could bundle any other thing that comes up (ie: potentially checking for accidental double // or double whack whack as i've heard it called 😸 ) inside that method

i'd maybe even go a step further, and set up iota/enum for different xdp capabilities and build a nice way of building these with code, maybe like:

api.BuildEndpoint(capabilities.Packetloss).Status() which returns a string or error or something, where the Packetloss iota type could then look up the url string etc. Would make ti really nice to test and not have strings all around, just a suggestion

Copy link
Member Author

Choose a reason for hiding this comment

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

Great suggestions Ramin, Thank you man. I'll go 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.

I will apply this in another PR as this PR is for the error stuff.
Would you please elaborate more on the .Status(), I am not sure if I got it properly.

Copy link

Choose a reason for hiding this comment

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

ah i'm definitely getting carried away so feel free to ignore me, or we can pair on it, but it the response is some kind of "string builder" that calling Status() on merely returns the endpoint string WITH the /status added, then its just a helper/builder that knows your url format, vs having to explicitly name a status endpoint of some kind

say BuildURL() returns a ApiEndpoint{ path: string } and func(a *ApiEndpiint) Status() could return the string, or add status: true to ApiEndpoint which implements a String() method that builds the full url.

Copy link
Member Author

Choose a reason for hiding this comment

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

oh got it, so that .Status was an example. Thanks man, that's good suggestion.

Copy link
Member

Choose a reason for hiding this comment

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

Keeping a single source of truth for URI paths sounds better if we want in the future to add more.
Having '/packet/status' concatenated to the endpointPrefix is not great for readability. Ramin's idea indeed sounds cool

sdk/client.go Outdated
resp, err := c.getResource(resPath)
if err != nil {
return nil, err
resp, resErr := c.getResource(resPath)
Copy link

Choose a reason for hiding this comment

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

i would definitely keep to just calling these err all the time as its easier to just scan them and ignore as "error handling boilerplate"

Copy link
Member Author

Choose a reason for hiding this comment

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

good catch, pushed a fix

}
msg := api.MetaMessage{}

if err := json.Unmarshal(resp, &msg); err != nil {
Copy link

Choose a reason for hiding this comment

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

i'd rely on http StatusCodes for this vs serialization success/failure and not try and overcomplicate it. Someone could return a 500 from api, but implement a MetaMessage response successfully and then this will be trouble

Copy link
Member Author

Choose a reason for hiding this comment

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

Well the API endpoints should return 200 status only on success of the operation. I'll update where it does it if there is any. So, the it will be easier for the SDK users to use it. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok I pushed a proposed fix for it.
Now it should not happen, in any case when an error is returned from the endpoint SDK follows that if status ok is returned it returns nil.

Copy link
Member

@Bidon15 Bidon15 left a comment

Choose a reason for hiding this comment

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

Nice work!

}

func (c *Client) BandwidthStart(req BandwidthStartRequest) error {
_, err := c.postResource("/bandwidth/start", req)
return err
return c.postServiceAction(endpointPrefix+"/bandwidth/start", req)
Copy link
Member

Choose a reason for hiding this comment

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

[nit]: consistency across endpointPrefix+ and endpointPrefix + is needed to be the same. fmt can help with this

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks I'll fix this one and the endpoint paths will be resolved in another PR.

}

func (c *Client) PacketlossStatus() (*MetaMessage, error) {
return c.getServiceStatus("/packetloss/status")
return c.getServiceStatus(endpointPrefix + "/packetloss/status")
Copy link
Member

Choose a reason for hiding this comment

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

Keeping a single source of truth for URI paths sounds better if we want in the future to add more.
Having '/packet/status' concatenated to the endpointPrefix is not great for readability. Ramin's idea indeed sounds cool

@mojtaba-esk mojtaba-esk merged commit e944605 into main Dec 8, 2023
8 of 9 checks passed
@mojtaba-esk mojtaba-esk deleted the mojtaba/sdk-error-enhancement branch December 8, 2023 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants