Skip to content
This repository has been archived by the owner on Sep 12, 2022. It is now read-only.

New Feature - Create and manage "Personal Access Tokens" (API Tokens) from "Settings Page" #789

Merged
merged 19 commits into from
Aug 27, 2018

Conversation

Tharon-C
Copy link
Contributor

@Tharon-C Tharon-C commented Aug 13, 2018

Create and manage "Personal Access Tokens" (API Tokens) from "Settings Page"

This PR is dependent on changes to Atmosphere API cyverse/atmosphere#648

This feature adds a section to the advanced settings page for creating and managing "Personal Access Tokens" or "API Auth Tokens" used to give other applications like the CLI access to the user's account.

screen shot 2018-08-13 at 10 02 27 am

This uses a new endpoint "<root>/access_tokens" and so a new store, actions, model, and collection have been added to the data layer.

Actions the user can take:

  • Create
  • Edit Name
  • Delete

Actions are submitted through modals:

APITokenCreate (two views)

Assign a name to Token

screen shot 2018-08-13 at 10 02 42 am

Success and Copy Token Hash

screen shot 2018-08-13 at 10 02 58 am

Transition, Assign and submit -> Success

screen shot 2018-08-13 at 10 02 58 am

APITokenEdit

screen shot 2018-08-13 at 10 10 58 am

APITokenDelete

screen shot 2018-08-15 at 2 28 56 pm

Checklist before merging Pull Requests

  • Fix APITokenDelete modal icon size to flex: "1 0 24px".
  • Add an entry in the changelog
  • Documentation created/updated (include links)
  • Reviewed and approved by at least one other contributor.

@calvinmclean
Copy link

cyverse/atmosphere#648

@calvinmclean
Copy link

calvinmclean commented Aug 13, 2018

I am testing how this works if the Atmosphere API is unreachable. When a token is being created, it will fail and the user will see that it is removed from the list, but the modal will stay open and it will keep saying 'Creating...'

I am experimenting with passing two callback functions to the create action, one for failure and one for success. Initially I just made the failure one change the state to not show creating anymore, and it worked out well. It just returned the user to the modal's initial state. Should we add an error message that shows up as part of the modal? Or we could use a NotificationController like we do with the icon select and guacamole color select

here is what I experimented with: calvinmclean@2983c70

If the Atmosphere API fails to create a token, the modal would still act as if it was trying to create the token.
Now, it will go back to the initial state of the modal (while still showing the entered name), and will present a notification with an error message.
@calvinmclean
Copy link

calvinmclean commented Aug 13, 2018

Also, should we display the expireTime in the UI? The Atmosphere API makes it available.
screen shot 2018-08-13 at 2 55 08 pm

@cdosborn
Copy link
Contributor

cdosborn commented Aug 15, 2018

Pr is failing the formatter
If you want to level up your git fu:
git rebase origin/master --exec 'npm run format -- -l'

This will replay your pr on top of origin/master, and after each commit is brought over, it will be tested with the formatter. If the formatter fails, it'll stop to let you amend the commit, then you can continue rebasing.

@Tharon-C
Copy link
Contributor Author

Tharon-C commented Aug 15, 2018 via email

@Tharon-C Tharon-C changed the title WIP: New Feature - Create and manage "Personal Access Tokens" (API Tokens) from "Settings Page" New Feature - Create and manage "Personal Access Tokens" (API Tokens) from "Settings Page" Aug 15, 2018
@cdosborn
Copy link
Contributor

cdosborn commented Aug 16, 2018

My thought is that we need to include the expiration time (and it should probably be 6mo). If it expired w/o the user knowing, they would experience this currently as the token just disappearing from their settings page. (left a comment on atmo pr, because it doesn't include expired tokens in api response).

import Utils from "./Utils";

export default {
create: ({name, atmo_user}, successCallback, failCallback) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Simplify this signature to

create(tokenName, user) => Promise

Since create is returning a promise, anyone who has the promise can promise.then(successCallback) or promise.catch(failCallback)

export default {
create: ({name, atmo_user}, successCallback, failCallback) => {
if (!name) throw new Error("Missing Token name");
if (!atmo_user) throw new Error("Missing Token author");
Copy link
Contributor

Choose a reason for hiding this comment

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

camelCase

errorMsg: "",
name: this.props.name || "",
successView: false
};
Copy link
Contributor

Choose a reason for hiding this comment

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

all state needs to be included here

mixins: [BootstrapModalMixin],
propTypes: {
user: React.PropTypes.number.isRequired
},
Copy link
Contributor

Choose a reason for hiding this comment

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

all props need to be included here

Copy link
Contributor Author

@Tharon-C Tharon-C Aug 17, 2018

Choose a reason for hiding this comment

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

In getInitialState we were assigning to name the name prop and letting in fall through to empty string if not defined name: name || "" . I removed the use of the name prop from Create modal as it was left over from when the Edit modal and Create modal were sharing code. Since separating these experiences into their own files we no longer need a name prop on the Create modal.

});
actions.APITokenActions.create(
attributes,
this.successCallback,
Copy link
Contributor

Choose a reason for hiding this comment

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

w.r.t to my prior comment,

let promise = actions.APITokenActions.create(...);
promise.then(...);
promise.catch(...):

Copy link
Contributor Author

@Tharon-C Tharon-C Aug 17, 2018

Choose a reason for hiding this comment

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

I'm having trouble getting this interface to work in a way that doesn't feel kludgy. Could you elaborate? The model returned doesn't seem to be a promise unless I make it one in the action and that is where things start to get awkward. I do think it is better than using callbacks just not sure the best way to do it.

I can do let promise = Promise.resolve(...) here but then I don't get the server response as an argument.

Copy link
Contributor

Choose a reason for hiding this comment

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

What does actions.APITokenActions.create return?

APITokenStore.remove(token);
token.destroy({
success: function() {
APITokenStore.emitChange();
Copy link
Contributor

Choose a reason for hiding this comment

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

emitChange is a store implementation detail, nothing outside the store should call this. It's an implementation detail, because its part of the inner-mechanics of how the store works that no user of the store need know about.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively you can dispatch to the store, and let the store do store stuff

};
},

updateState() {
Copy link
Contributor

Choose a reason for hiding this comment

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

copy pasta

render() {
let {APITokenStore} = this.props.subscriptions,
profile = this.state.profile,
api_token = APITokenStore.getAll();
Copy link
Contributor

Choose a reason for hiding this comment

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

camelCase, and variable doesn't make sense for what should be a collection

{
token
},
() => {}
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to pass empty callbacks, leave off that arg

<tbody>
{api_token
? api_token.map(this.renderTokenRow)
: []}
Copy link
Contributor

@cdosborn cdosborn Aug 16, 2018

Choose a reason for hiding this comment

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

Suggestion: (tokens || []).map(this.renderTokenRow)

@Tharon-C
Copy link
Contributor Author

RE: Showing the expiration date
I had made mockups and sought approval from Edwin on the feature before starting work. The lack of expiration date did come up but was decided to treat them as persistent for now as an MVP.

We are modeling the functionality of GH's implementation of "Personal Access Tokens".

// Add token optimistically
Utils.dispatch(APITokenConstants.ADD_TOKEN, {apiToken});

apiToken
Copy link
Contributor

Choose a reason for hiding this comment

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

Change this method so that a promise is returned.

let promise = apiToken.save()
promise.done(...)
promise.fail(...)
return promise

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This does prove that the model is indeed a promise and removes the then is not a function error I was getting before. then however isn't getting called... I'll keep digging.

Copy link
Contributor

Choose a reason for hiding this comment

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

The model is not a promise

Copy link
Contributor

Choose a reason for hiding this comment

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

@cdosborn
Copy link
Contributor

Okay, then lets not have the tokens expire, it was just an edge case I was considering.

@cdosborn
Copy link
Contributor

Optimistic logic isn't ideal. When the user hits create, the token is added, and shows in the table, but the modal continues to show in progress. So while the table add is optimistic the modal is not optimistic. We do not need to optimistically add the token, since the use will have to wait to copy the token key.

Utils.dispatch(APITokenConstants.REMOVE_TOKEN, {apiToken});
NotificationController.error(
"Error creating token.",
"Your login might be expired. If you continue to see this error " +
Copy link
Contributor

Choose a reason for hiding this comment

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

Notification controller needs to include the error from the create response. Take a look at the arg to catch.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can add this change

@Tharon-C
Copy link
Contributor Author

RE: Optimistic logic
Yeah, I can agree with that.

@cdosborn
Copy link
Contributor

cdosborn commented Aug 22, 2018

user_id attribute doesn't need to be included in the create call. Since the user must be authenticated (i.e. the api knows exactly who is trying to create the token) to create the token, the api already knows who to create the token for.

@Tharon-C
Copy link
Contributor Author

RE: user_id
OK, I removed the user from the api request and "all related things"
Changes to Atmo are required calvinmclean/atmosphere#1

@Tharon-C
Copy link
Contributor Author

All requested changes have been made. Once cyverse/atmosphere#648 has been merged this can be as well. Thanks @cdosborn and @calvinmclean

@cdosborn
Copy link
Contributor

cdosborn commented Aug 27, 2018

Added "Issued" column re: cyverse/atmosphere#648 (comment)

2018-08-27-134616_2560x1440_scrot

@cdosborn
Copy link
Contributor

I tested against an api throwing errors in create/update/delete

@Tharon-C
Copy link
Contributor Author

Thanks @cdosborn the updates look good!

@cdosborn cdosborn merged commit 657ddec into cyverse:master Aug 27, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants