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

warden/groups: add a PUT method to overwrite members & some refactoring #768

Closed
wants to merge 5 commits into from
Closed

warden/groups: add a PUT method to overwrite members & some refactoring #768

wants to merge 5 commits into from

Conversation

zepatrik
Copy link
Member

@zepatrik zepatrik commented Feb 7, 2018

There was no way to overwrite all group members meaning just sending the actual value, instead one had to keep track of changes or remove all members to add the new ones again.
Now a http PUT method allows to set the members array.

closes #745

There was no way to overwrite all group members meaning just sending the actual value, instead one had to keep track of changes or remove all members to add the new ones again.
Now a http PUT method allows to set the members array.
@zepatrik
Copy link
Member Author

zepatrik commented Feb 7, 2018

not quite ready to merge, just for review if I am doing it right


if err := json.NewDecoder(r.Body).Decode(&g); err != nil {
h.H.WriteError(w, r, errors.WithStack(err))
if err := h.checkRequest(r, h.PrefixResource(GroupsResource), "create"); err != nil {
Copy link
Member Author

Choose a reason for hiding this comment

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

isn't it better to always first check the request?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, maybe I was using g here for some reason which was why it was decoded first, but as it stands now the order can be reversed´the way you did it.

if err := h.Manager.RemoveGroupMembers(id, m.Members); err != nil {
w.WriteHeader(http.StatusNoContent)
}

Copy link
Member Author

Choose a reason for hiding this comment

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

missing comment

return nil
}

func (m *SQLManager) applyInTransaction(requests ...func(tx *sqlx.Tx) error) 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.

check if rollbacks are correct

@zepatrik
Copy link
Member Author

zepatrik commented Feb 7, 2018

shouldn't this function (and the next one) be non capital because it is not checking the request at all, it is just a helper right?

@aeneasr
Copy link
Member

aeneasr commented Feb 7, 2018

shouldn't this function (and the next one) be non capital because it is not checking the request at all, it is just a helper right?

I'm not sure right now, I think they need to be upper case for swagger docs to work. I'd have to check, otherwise all of the handlers can be lower case.

Copy link
Member

@aeneasr aeneasr left a comment

Choose a reason for hiding this comment

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

Looks pretty solid so far!

w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) OverwriteGroupMembers(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
Copy link
Member

Choose a reason for hiding this comment

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

I would probably rename it to "UpdateGroup"

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 think I'm going for UpdateGroupMembers because the http PUT url is GroupsHandlerPath + "/:id/members" so only one group is affected, not all of them


if err := json.NewDecoder(r.Body).Decode(&g); err != nil {
h.H.WriteError(w, r, errors.WithStack(err))
if err := h.checkRequest(r, h.PrefixResource(GroupsResource), "create"); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Yes, maybe I was using g here for some reason which was why it was decoded first, but as it stands now the order can be reversed´the way you did it.

return nil
}

func (m *SQLManager) applyInTransaction(requests ...func(tx *sqlx.Tx) error) error {
Copy link
Member

Choose a reason for hiding this comment

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

requests are really transactions, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

A transaction is a sequence of requests I would rather say.

Copy link
Member

Choose a reason for hiding this comment

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

How about executors?


for _, req := range requests {
if err := req(tx); err != nil {
tx.Rollback()
Copy link
Member

Choose a reason for hiding this comment

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

If rollback fails, the error should be returned instead

if err := tx.Rollback(); err != nil {
  return err
}

@@ -183,3 +187,33 @@ func (m *SQLManager) ListGroups(limit, offset int) ([]Group, error) {

return groups, nil
}

func (m *SQLManager) OverwriteGroupMembers(group string, members []string) error {
if err := m.applyInTransaction(m.deleteGroup(group), m.createGroup(group), m.addGroupMembers(group, members)); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

this can be simplified to return m.ApplyIn...

return func(tx *sqlx.Tx) error {
for _, subject := range subjects {
if _, err := tx.Exec(m.DB.Rebind("DELETE FROM hydra_warden_group_member WHERE member=? AND group_id=?"), subject, group); err != nil {
if err := tx.Rollback(); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

You have duplicate rollbacks here, let applyInTransaction handle rollbacks instead!

return func(tx *sqlx.Tx) error {
for _, subject := range subjects {
if _, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group_member (group_id, member) VALUES (?, ?)"), group, subject); err != nil {
if err := tx.Rollback(); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Rollback is already covered by applyInTransaction

func (m *SQLManager) createGroup(group string) func(tx *sqlx.Tx) error {
return func(tx *sqlx.Tx) error {
if _, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group (id) VALUES (?)"), group); err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

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

Normal errors do not have stack traces in go, to add a stack trace do return errors.WithStack(err)

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 now I got it, I thought that errors.WithStack(err) just has to be called once to get the whole stacktrace.

Copy link
Member

Choose a reason for hiding this comment

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

Exactly, and it also needs to be called at the location which is the deepest in the stack trace and there only once - otherwise you'll overwrite the stack trace. This is a bit tricky unfortunately. The rule is that we wrap all errors coming from other libraries but don't wrap errors coming from our own. That way we ensure that traces are always as deep as possible in the stack.

Copy link
Member Author

Choose a reason for hiding this comment

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

So this should be actually without a stacktrace?

func (m *SQLManager) deleteGroup(id string) func(tx *sqlx.Tx) error {
return func(tx *sqlx.Tx) error {
if _, err := tx.Exec(m.DB.Rebind("DELETE FROM hydra_warden_group WHERE id=?"), id); err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

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

Normal errors do not have stack traces in go, to add a stack trace do return errors.WithStack(err)

if err := tx.Rollback(); err != nil {
return err
}
return err
Copy link
Member

Choose a reason for hiding this comment

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

Normal errors do not have stack traces in go, to add a stack trace do return errors.WithStack(err)

@zepatrik
Copy link
Member Author

ready for review @arekkas

@aeneasr aeneasr self-requested a review February 17, 2018 08:54
Copy link
Member

@aeneasr aeneasr left a comment

Choose a reason for hiding this comment

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

Totally forgot about this one, have some questions. I'll check out the SQL implementation in more detail because the changes are a bit complex.

// oauth2: hydra.warden.groups
//
// Responses:
// 204: emptyResponse
Copy link
Member

Choose a reason for hiding this comment

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

PUT requests typically don't return 204 responses but instead 200 + body. 204 is typically used for DELETE responses to aknowledge that the resource is gone

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 may not have changed everything correctly when copying the comments

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 also in the response, see w.WriteHeader(http.StatusNoContent)

return func(tx *sqlx.Tx) error {
_, err := tx.Exec(m.DB.Rebind("INSERT INTO hydra_warden_group (id) VALUES (?)"), group)

return errors.WithStack(err)
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if this works for nil values? So if error is nil, does this return nil as well or not?

Copy link
Member Author

Choose a reason for hiding this comment

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

https://godoc.org/github.com/pkg/errors#WithStack
I looked it up it works as expected

Copy link
Member

Choose a reason for hiding this comment

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

@aeneasr aeneasr changed the base branch from master to 0.11 April 8, 2018 21:49
@aeneasr aeneasr changed the base branch from 0.11 to master April 8, 2018 21:50
@aeneasr
Copy link
Member

aeneasr commented May 20, 2018

This is now tracked as ory/keto#10

@aeneasr aeneasr closed this May 20, 2018
aeneasr pushed a commit to ory/keto that referenced this pull request May 23, 2018
* transfer UpdateRoleMembers from ory/hydra#768 to keto

* fix tests by using right http method & correcting sql request

* Change behavior to overwrite the whole role instead of just the members.
+ small sql migration fix
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.

Add PUT method for /warden/groups/:id
2 participants