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
Fixes #36015 - API endpoints to add/remove single roles #597
Fixes #36015 - API endpoints to add/remove single roles #597
Conversation
186eda3
to
8434de4
Compare
api :DELETE, '/hostgroups/:id/remove_ansible_roles', | ||
N_('Remove directly assigned Ansible roles from a hostgroup') | ||
param :id, :identifier, :required => true | ||
param :ansible_role_ids, Array, |
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.
How do you pass an array of ansible_role_ids? I wasn't able to do that (but it works with just one ansible_role_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.
You can simply use JSON object with array: { "ansible_role_ids": [1,2,3] }. Accuracy depends on what you use. Since curl
needs to be pretty verbose, I use Postman
Chrome extension which makes API testing quite easy and straightforwad.
host = FactoryBot.create(:host, | ||
:managed => false, | ||
:ansible_role_ids => [@ansible_role3.id, @ansible_role2.id, @ansible_role1.id]) | ||
post :remove_ansible_roles, |
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.
The only thing I couldn't figure out yet is how the tests are passing even though it is a post request (same for hostgroup). Should it be a delete request?
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.
That's a good question... :/
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 @adamruzicka describes it pretty well here
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'm not commenting inline, since /add_X
and remove_x
are used in both the hostgroup and the hosts controllers. It's not a very REST thing include the action in the endpoint: the HTTP verb describes what you want.
In REST you have collections and resources. A collection is /hosts
and a resource is /hosts/:id
. Then the HTTP verb describes the action.
More practical, that means you have a collection /hosts/:id/ansible/roles
. You can GET
them to get a listing, POST
to it to create a new link, which you also direct you to the individual resources.
The individual resources would likely be on /hosts/:host_id/ansible/roles/:role_id, which you can
GETto view and
DELETEto remove. If you want to update it,
PUT` is typically used.
https://restfulapi.net/resource-naming/ does mention that sometimes you do have a more procedural concept, but in this case it maps very well to collections and resources.
Now I will note you here add and delete collections, but https://restfulapi.net/http-methods/#delete really focuses on individual resources. That's what REST is all about. The PR summary describes you say add/remove single roles, so per resource is probably sufficient for that. If that's really what you want to support bulk actions, I'm not sure REST is correct. If you want it in the UI, the graphql API may be more appropriate than REST.
8434de4
to
c7b4e6a
Compare
:required => true | ||
|
||
def add_ansible_role | ||
process_response @hostgroup.ansible_roles << @ansible_role |
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.
What if the role is already in there? Is ansible_roles
a set or a list?
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 the role is already assigned, it will return
{
"error": {
"message": "Validation failed: Ansible role has already been taken"
}
}
with 500
error code.
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.
A HTTP 500 sounds bad. I'd consider making it 422 if you consider it wrong or 200 if you think it's OK (and I'd use 200 Ok).
You can also consider an API endpoint PUT /hostgroups/:id/ansible_roles/:ansible_role_id
which should be idempotent: it ensures it's part of the set.
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 agree that 500
code is weird for such response, but Rails will treat this as a validation error anyway. I left 422
instead of suppressing the error. If you still find this wrong, I can change it to suppress the ActiveRecord::RecordInvalid
and return 200
.
:ansible_role_id => [@ansible_role2.id] | ||
}, | ||
:session => set_session_user | ||
assert_response :success |
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'd expect HTTP 201 Created if it was added, not HTTP 200 Success
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.
The server responds with correct 201
.
P.S. Apparently post
and others are expecting :action
instead of route from routes
definition. ofedoren.experience += 1
. Also, assert_response
here considers both 200
and 201
as successful via mapping, that's why the test doesn't fail, but I've changed to use the specific number instead.
P.P.S. I hate test frameworks :/
:ansible_role_id => [@ansible_role2.id] | ||
}, | ||
:session => set_session_user | ||
assert_response :success |
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.
HTTP 204 (No Content) is common after a DELETE. If it's 200 Success, I wonder what's in the content.
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.
We have the same behaviour with responses almost everywhere else since we use the same logic/methods. In this case the server will respond with 200
and the modified entity (hostgroup in this case).
assert_equal assigns('hostgroup').ansible_roles, [@ansible_role1, @ansible_role2] | ||
end | ||
|
||
test 'should remove only specified roles from a hostgroup' do |
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.
Another use case: what if the role doesn't exist? Does it return 404 or 200/204 since it's not part of the role collection anymore? I'd expect the latter.
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.
The server will respond with 200
.
e6eddf4
to
c1d6084
Compare
[test foreman_ansible] Oh, right, github actions... There really should be a button/bot command to re-run the tests :/ |
c1d6084
to
e34fca1
Compare
e34fca1
to
ecc2007
Compare
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.
LGTM. Everything works as expected.
@ekohl can I merge? :)
I will merge it to ensure it's included in the next released version. |
No description provided.