-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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: GraphQL address add mutation #4068
Conversation
…ay-gql-add-address
Added WIP back since the address response wasn't fully transformed |
@aldeed Fixed the address response, should be good for the initial testing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, but a couple concerns about proper error handling @mikemurray
* @param {Object} context - an object containing the per-request state | ||
* @return {Object} AddAccountAddressBookEntryPayload | ||
*/ | ||
export default function addAccountAddressBookEntry(_, { input }, context) { | ||
const { accountId, address, clientMutationId = null } = input; | ||
const dbAccountId = decodeAccountOpaqueId(accountId); | ||
const dbAccountId = accountId && decodeAccountOpaqueId(accountId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add at the top of decodeOpaqueIdForNamespace
:
if (opaqueId === undefined || opaqueId === null) return null;
And then we don't need this accountId &&
check everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Is it possible to go a level deeper and put that check in decodeOpaqueId
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ticean decodeOpaqueId
is not used in decodeOpaqueIdForNamespace
. They are very identical methods though.
export const decodeOpaqueId = (opaqueId) => {
const unencoded = Buffer.from(opaqueId, "base64").toString("utf8");
const [namespace, id] = unencoded.split(":");
return { namespace, id };
};
export const decodeOpaqueIdForNamespace = curry((namespace, opaqueId, error = new Error(`ID namespace must be ${namespace}`)) => {
const unencoded = Buffer.from(opaqueId, "base64").toString("utf8");
const [actualNamespace, id] = unencoded.split(":");
if (actualNamespace !== namespace) throw error;
return id;
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per the discussion in stand-up today, if we are going to explicitly require the accountId
to be passed in, then the check accountId && decodeAccountOpaqueId(accountId)
is no longer necessary.
If opaqueId
is undefined
, Buffer.from
will throw an error that could be handled by the client, though it's vague. (Error message example: "First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object."
)
So...
- Would you rather return
null
ifopaqueId
is not defined - Leave as is and let
Buffer.from
throw the error - throw a nicer error if
opaqueId
comes inundefined
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not destructure from a decodeOpaqueId()
result inside decodeOpaqueIdForNamespace
? Then you can put the check in decodeOpaqueId
and keep it nice and DRY.
server/methods/accounts/accounts.js
Outdated
return updatedAccount.profile.addressBook.find((updatedAddress) => address._id === updatedAddress._id); | ||
} | ||
|
||
return result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to update the jsdoc @return
. Also, it's now more confusing to return result
because you don't know which object structure to expect. I'm thinking just return false
for this final result, as long as our current client calls will handle that properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we return false, then anywhere that looks for if(error)
will not trigger, and anywhere that looks for if(result)
will do nothing, leaving us in some nebulous state. It should probably just throw an error if it gets that far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aldeed apologies if I'm misunderstanding this comment. are you suggesting to return false
in place of the updated address result? We do need to return the updated address in GraphQL and, more generally, return values for all operations. That was a decision made early in the the first round of API design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've decided to throw an error, instead of returning false
in the method. That seems to be handled well by the resolver. (Unless you wanted it the return false here for other reasons?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Regarding my comment above, Eric explained on our standup that this is a condition where the upsert didn't change anything for some reason and would be a rare case. Agree that we shouldn't return the Mongo response there. That the address is returned in the happy path which was my only concern. Thanks!
...updatedAddress, | ||
_id: encodeAccountOpaqueId(updatedAddress._id) | ||
}, | ||
address: xformAddressResponse(updatedAddress), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comment about the method return value. If you change that to return false, then you should check here. I'm thinking if (!updatedAddress)
then you should probably throw an error so that it's clear to the client that the add was unsuccessful. It'll have to be a rather vague error message because we won't know why it failed, but it's also unlikely to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I threw the error in "accounts/addressBookAdd", instead of returning false
, so if the update failed for some rare reason, this we'll never get to this point and the error will show in the response.
@aldeed Ready for a re-review |
Resolves #3919
Impact: minor
Type: feature
Issue
Update the existing
addAccountAddressBookEntry
and fix any issues preventing it from working properly.Solution
Most of the implementation was already there. I made some updates to ensure it works as intended.
accounts/addressBookAdd
to return the added addressaddAccountAddressBookEntry
mutationBreaking changes
Should be none; however, the
accounts/addressBookAdd
was updated to return the added address. No methods depended on a specific return by that method, only that result was not undefined, which this satisfies.Testing
localhost:3000/graphiql