HTTP verb for enrolling student in an exam - rest

What would be the idiomatic HTTP method to use when enrolling a student in an exam? How would the endpoints be structured - [Verb] "exams/students" or a [Verb] "students/exams" or something entirely different?
The context is as follows:
A student can select an exam attend.
The student should then be enrolled in the exam if not already enrolled and if the exam is currently active.
I'm guessing that if the event is not active then some 400-range status should be returned - perhaps 409 or 422.
Not sure what to return if the event is active but the student is already attending the exam? Some 200 status or some 400 status?
If the student gets enrolled then the exam should be returned to the caller with a 200 range status.
I apologize if this has been asked before.

Related

Which http status code should be returned when the identifier in the payload does not exist?

I've the endpoint POST /permission-period which should create a permission period for a specific user.
The payload:
{
"userId": 10,
"permissionPeriodDateFrom": "2017-12-01",
"permissionPeriodDateTo": "2017-12-10"
}
If any property from the payload is invalid, i usually return 422 Unprocessable Entity, which means the syntax of the request payload is valid but it cannot be processed due to invalid data.
What status should I return if the user does not exist and I do not want to provide the client with this security related information? Should i expose the message that the user does not exist or not?
I think HTTP status code wise I would choose a 400 - Bad request. Regarding the returned error message I would give the user a helpful information while keeping security-related information secret.
You could return something like The given userId is either malformed, does not exist or cannot be linked to the posted resource. This would allow the user to identify the spot where in the body the error comes up (property userId) but does not tell him the exact error to prevent user enumeration.
The good news is that you obviously have a protected endpoint when creating a permission-period so the API user is identifiable and you can take other actions for preventing user enumeration and related brute force attacks such as consumer-based throttling or locking the API consumer after x attempts.
I hope my notes help you with your API design.

Restful API design: How to lock records on GET collection?

The idea
Using the API platform's framework I have a case where I need to "lock access/view" to a collections of resources for the first client that requested them preventing them from being again retrieved by other client (a race condition) until the first one finishes/updates their status.
Why: because two clients cannot process (email to a customer for example) the same coupon since coupons can be used only 1 time.
Example
Imagine we have a collection of coupon codes consumed by many clients: Once a collection of coupon codes is requested by a client, no other client can retrieve the same coupon codes until the first client tags (POST/PATCH) each coupon code with "consumed" for example.
The challenge
Of course I can lock those coupons on the GET request but this is against Restful API paradigm: a GET cannot modify the resource state, Get is supposed to be idempotent and safe.
Suggestions
ETag optimistic locking: Cannot use this on collections.

HTTP status code for inability to process request

Making a delete request to /api/ingredients/123 endpoint.
If ingredient 123 didn't exist, I expect I should return a 404 Not Found status code. Agree?
What if ingredient 123 did exist, but was used in an existing recipe so it couldn't be deleted. What status code should be returned?
409 Conflict
The request could not be completed due to a conflict with the current
state of the resource. This code is only allowed in situations where
it is expected that the user might be able to resolve the conflict and
resubmit the request. The response body SHOULD include enough
information for the user to recognize the source of the conflict.
Ideally, the response entity would include enough information for the
user or user agent to fix the problem; however, that might not be
possible and is not required.
In your case, an ingredient cannot be deleted as it is used (conflicting state of the resource) in one or more recipes. But once the ingredient is out of use it can be deleted.
Re: HTTP status code in response to the condition that ingredient 123 didn't exist [and never did exist]; yes, I agree that the 404 - Not Found - is correct.
If ingredient 123 did exist and is now deleted, then 410 - Gone - is appropriate. (My website uses the 410 status code for obsolete products.)
However, your case has ingredient 123 existing, but that the delete request for that specific ingredient cannot be processed due to a data dependency conflict ("an existing recipe"). For this case, 405 - Method Not Allowed - is appropriate.
HTTP status of 405 indicates that the ingredient reference is OK, but not the delete request.
Wikipedia and w3.org have helpful pages on HTTP status codes.
304 Not Modified, perhaps? Or, 412 Precondition Failed.
There's a list of status code definitions here: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
I think any answer to this question would be 30% technical, 70% opinion.

Restful URI design

Let's say that the domain structure of anapplication is as follows:
There is domain object called Department.
There is a domain object called Student.
There is a domain object called Paper.
The relationship between Student and Department is many-to-many.
A student can publish (create) a Paper for himself or for a
particular Department.
A student can view all the papers published by him for
himself and for departments to which he belongs (the latter includes
papers published by other students belonging to the same department
as the given student)
Here is what I think the restful uri designs should be like
Student creates (POST) a white paper for himself :
/students/{studentid}/papers
Student creates (POST) a white
paper for a particular department
/students/{studentid}/departments/{departmentid}/papers
Get all student papers published by him for himself
/students/{studentid}/papers/self
Get all student papers published by him for himself including the papers
of the departments to which he belongs
/students/{studentid}/papers
Similar get requests for point number 1 and 2.
The other way to arrive at the above end points would be something like (considering only points 1 and 2) :
/students/{studentid}/papers
and then pass departmentid in the request body. The application would the check for the presence of departmentId in the request. If it's not null then it will assume that this paper is being published for the given departmentid, otherwise for the student himself.
Which one of the above would be a better approach?
This link could help you to design your RESTful service: https://templth.wordpress.com/2014/12/15/designing-a-web-api/.
In addition, here are my comments regarding your URLs:
Everything that identifies your resource should be within the resource path (for example departmentid)
Regarding relations, we need to identify which URLs will handle references. For example, /students/{studentid}/departments/{departmentid}/papers will allow to attach an existing paper to a department or create a new one and in addition attach it to the department
I don't understand this url: /students/{studentid}/papers/self especially the token self. Does self refer to the current authenticated user? If so, I think that should use a query parameter since it doesn't really correspond to a resource... In fact, you rather use query parameters for list filtering
Hope it helps you,
Thierry
Since departmentid is part of how a resources is identified, it must be part of the URL. Putting it into the request body is a violation of REST principles.

Proper status code for data that is well formed but invalid because of system state

I have a system where users are represented primarily with an integer ID. I have a resource; let's call it X. X is associated with 2 users: one created X and the other will approve X when the creator is done. X's approver is selected by the creator when it is submitted via POST (or can be edited in later), and the request identifies the approver by user ID. There's one additional restriction: approvers and creators are paired together. Approvers can only approve X if the creator is assigned to them.
So I have a few possible failure cases regarding the approver's user ID in the request:
Malformed ID (non-integer)
Approver ID is an integer, but no user with that ID exists.
Approver ID is an existing user, but that user doesn't have the approver role.
Approver ID is an existing approver, but the approver is not assigned to the creator.
400 is obviously the correct status code for case 1, but it doesn't seem like the right status code for 2-4. 400 designates a malformed request, but 2-4 are problems specifically with existing data, not with parsing the request. I considered 409, but that seems to be a problem with the resource itself. This is a problem with additional resources that are related to resource X. I also thought about 406, but that seems to be geared toward providing content in an unknown format (like XML when only JSON is accepted).
So what status code is appropriate to indicate that the client provided well formed but bad data?
Note that for clarity to clients you will always include an explanation of the error, so a slightly inexact code with an appropriate message will most certainly be helpful.
That being said, I would use 404 (Not Found) for 1 and 2. Integer or not, a resource with that ID does not exist.
Both 3 and 4 seem localized to our application, so I would use 400. 403 could be used for 3, but that might imply authentication problems.
I agree with #Will for 1 & 2 - 404.
For 3 & 4, I would go with 409. Since in the general case (you said that the approver can be changed later) there's no real distinction (that I can see) between:
Approver ID is an existing user, but that user doesn't have the approver role.
and
Approver ID is an existing user, and 5 seconds ago they were the approver, but that is no longer the case
So it feels the same as an edit conflict.