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

exp/services/recoverysigner: remove type, add identities #2323

Merged
merged 4 commits into from
Feb 26, 2020
Merged

exp/services/recoverysigner: remove type, add identities #2323

merged 4 commits into from
Feb 26, 2020

Conversation

leighmcculloch
Copy link
Member

@leighmcculloch leighmcculloch commented Feb 26, 2020

PR Checklist

PR Structure

  • This PR has reasonably narrow scope (if not, break it down into smaller PRs).
  • This PR avoids mixing refactoring changes with feature changes (split into two PRs
    otherwise).
  • This PR's title starts with name of package that is most changed in the PR, ex.
    services/friendbot, or all or doc if the changes are broad or impact many
    packages.

Thoroughness

  • This PR adds tests for the most critical parts of the new functionality or fixes.
  • I've updated any docs (developer docs, .md
    files, etc... affected by this change). Take a look in the docs folder for a given service,
    like this one.

Release planning

  • I've updated the relevant CHANGELOG (here for Horizon) if
    needed with deprecations, added features, breaking changes, and DB schema changes.
  • I've decided if this PR requires a new major/minor version according to
    semver, or if it's mainly a patch change. The PR is targeted at the next
    release branch if it's not a patch change.

What

Remove the type parameter stored alongside registered accounts and replace it with exposing information about which identities are set on an account to clients.

Remove the type request and response parameters. Add the identities response parameter, and replace owner_identities and other_identities with identities.owner and identities.other.

Why

The type existed purely for clients to get some sense of what purpose an account was registered, but it's really meta data about how a client is using the service and isn't necessarily a core concept for the
service.

The value was also synonymous with which identities are configured for an account. A type of personal will always have only an owner identity, and a type of share was always going to have an other identity alone or in addition to owner.

By removing the concept of type and instead just giving a client visibility into which identities are set on the account we reduce the number of concepts a client has to learn and know about which makes the API simpler, even if the change appears to make the API slightly more complex.

The reason I replaced owner_identities and other_identities is while thinking about the right way to expose the identities to the client it occurred to me the simplest method for a JavaScript client would be to have the identities be keys of an object, and so I structured the response that way, and it made sense to also modify the request to match.

Known limitations

This is a breaking change to the API, but there are no consumers and the API is experimental.

### What
Remove the `type` parameter stored alongside registered accounts and
replace it with exposing information about which identities are set on
an account to clients.

### Why
The `type` existed purely for clients to get some sense of what purpose
an account was registered, but it's really meta data about how a client
is using the service and isn't necessarily a core concept for the
service.

The value was also synonymous with which identities are configured for
an account. A `type` of `personal` will always have only an `owner`
identity, and a `type` of `share` was always going to have an `other`
identity alone or in addition to `owner`.

By removing the concept of `type` and instead just giving a client
visibility into which identities are set on the account we reduce the
number of concepts a client has to learn and know about which makes the
API simpler, even if the change appears to make the API slightly more
complex.
Comment on lines +48 to +56
Address: acc.Address,
Identities: accountResponseIdentities{
Owner: accountResponseIdentity{
Present: acc.OwnerIdentities.Present(),
},
Other: accountResponseIdentity{
Present: acc.OtherIdentities.Present(),
},
},
Copy link
Member Author

@leighmcculloch leighmcculloch Feb 26, 2020

Choose a reason for hiding this comment

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

You'll notice this code block is duplicated a few times. I could reduce some code here, but I'm going to expend more effort holistically looking at reducing the duplicity here and in the authentication. Follow up: #2324.

@howardtw
Copy link
Contributor

👀

}

type accountResponseIdentities struct {
Owner accountResponseIdentity `json:"owner"`
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can make these two fields omitempty instead of using the Present bool in another struct. If there's one field presented in the accountResponseIdentities, then the client should be able to tell whether it's owner or other.

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 like that too, but our frontend team have mentioned before that it's much easier from a clients perspective is fields are always present and have a clear signal that something isn't available.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. If that's the case, would making it IsOwner bool and IsOther bool be sufficient?

Copy link
Member Author

@leighmcculloch leighmcculloch Feb 26, 2020

Choose a reason for hiding this comment

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

It is another way to solve the same goal, but it doesn't communicate the same intent. The account is not an owner, or an other, it has an owner identity, or an other identity.

If we ever want to expose more details about the owner or other the object let's us expand by adding more fields alongside present.

Do you have any specific concerns?

Copy link
Contributor

Choose a reason for hiding this comment

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

I usually avoid having nested objects that are too deep. However, if the goal is to incorporate more fields in the future, it makes sense. I still feel like the cleanest way is to make it the way I suggested in the first place as Present feels redundant. Let me confirm with the frontend team again.

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 like the omitempty approach too. If I do that, what do we do when the thing is present but has no fields? It will be like this: "owner": {} which seems a bit odd and I think is harder to test for in JavaScript? Being able to do owner.present for true/false explicitly seems really simple.

Copy link
Contributor

Choose a reason for hiding this comment

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

It will be like this: "owner": {} which seems a bit odd and I think is harder to test for in JavaScript.

Maybe we can make the backend never do that?

Being able to do owner.present for true/false explicitly seems really simple.

Sure. It's not a blocker. Approving it.


// Present indicates if the Identities contains at least one identity. Returns
// false if it is an empty/zero value.
func (i Identities) Present() bool {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I personally like leading bool methods with Is, just to make things clearer:

Suggested change
func (i Identities) Present() bool {
func (i Identities) IsPresent() bool {

Copy link
Member Author

@leighmcculloch leighmcculloch Feb 26, 2020

Choose a reason for hiding this comment

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

I've liked this pattern too in other languages in the past, but in Go code it's more common to omit the Is. Grabbing a random example the Response type has Close and Uncompressed, but there are loads of examples of this in the stdlib and other packages. Thankfully with types it is really clear that the returned value is a boolean. Let me know if this doesn't address your concern.

Copy link
Contributor

@howardtw howardtw left a comment

Choose a reason for hiding this comment

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

Approving it. Note that there are still a couple of things that can potentially be improved like #2323 (comment), https://github.com/stellar/go/pull/2323/files#r384694003, and https://github.com/stellar/go/pull/2323/files#r384679399. We can address some of them in other PRs tho.

@leighmcculloch leighmcculloch merged commit c19fcdc into stellar:master Feb 26, 2020
@leighmcculloch leighmcculloch deleted the goodbye👋typesolongpeaceout✌️ branch February 26, 2020 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants