API=PATCH:WorkItem.2 (follow jsonapi spec) #482
Conversation
|
I believe fields == attributes "data": {
"attributes": {
"system.creator": "user-ref",
"system.state": "new",
"system.title": "Example story",
"version": 5
},
"id": "42",
} |
|
@aslakknutsen , That is possible, but then WIT type and Version will be on the same level of title,description etc, state, etc. That differs from model definition. I understand jsonapi spec do not say to follow model definition but... // WorkItem represents a work item as it is stored in the database
type WorkItem struct {
gormsupport.Lifecycle
ID uint64 `gorm:"primary_key"`
// Id of the type of this work item
Type string
// Version for optimistic concurrency control
Version int
// the field values
Fields Fields `sql:"type:jsonb"`
} |
0adee00
to
293ddd7
Compare
Current coverage is 67.52% (diff: 84.29%)@@ master #482 diff @@
==========================================
Files 62 63 +1
Lines 3286 3403 +117
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
+ Hits 2193 2298 +105
- Misses 857 862 +5
- Partials 236 243 +7
|
|
@pranavgore09 Sure, Model != REST.
|
|
@aslakknutsen @tsmaeder I have addressed above comments except As of now. attributes can accept anything. But Should we rename |
|
Type should be a relationship. On Wed, Nov 16, 2016, 08:48 Pranav Gore notifications@github.com wrote:
|
{
"data": {
"attributes": {
"system.state": "new",
"system.title": "Example story",
"version": "1"
},
"id": "42",
"relationships": {
"assignee": {
"data": {
"id": "6c5610be-30b2-4880-9fec-81e4f8e4fd76",
"type": "identities"
}
},
"baseType": {
"data": {
"id": "system.userstory",
"type": "workitemtypes"
}
}
},
"type": "workitems"
}
}ToDo:
|
|
@aslakknutsen @tsmaeder : If I go with Currently our path URL is - |
| a.Attribute("type", d.String, func() { | ||
| a.Enum("workitems") | ||
| }) | ||
| a.Attribute("id", d.String, "ID of the work item which is being updated", func() { |
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.
Just like in https://github.com/almighty/almighty-core/blob/master/design/user_types.go#L25
we do not include the ID in the payload because we do it allow updating the ID, and that it could be retrieved from the REST URL, I am wondering if we should include it 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.
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.
Got it, Thanks. If the spec needs it, we would need to keep it.
Could we ensure that the workitem doesn't get updated with whatever ID is present in the payload? Or, are we validating that the same ID is present ( as that in the rest url ) ?
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.
good point, I checked goadesign is not performing any db check for existence of item, we are doing that in repository methods. So no need to validate if they are same, we will use only from payload.
this spec tells us that ID should be present in url as well as payload.
Thanks, I will update same. https://github.com/almighty/almighty-core/pull/482/files#diff-d2f956a730e7c36b29bc0dceedd367bdR173 should be updated.
| uuidStr := assigneeData.ID | ||
| assigneeUUID, err := uuid.FromString(uuidStr) | ||
| if err != nil { | ||
| return nil, NewBadParameterError("data.relationships.assignee.data.id should be UUID", uuidStr) |
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 first argument of the NewBadParameterError should be the paramater name.
The error message would be constructed internally.
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.
thanks, done in dbc7935
| } | ||
| _, err = identityRepo.Load(ctx, assigneeUUID) | ||
| if err != nil { | ||
| return nil, NewBadParameterError("data.relationships.assignee.data.id not found", uuidStr) |
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 first argument of the NewBadParameterError should be the paramater name.
The error message would be constructed internally.
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.
thanks, done in dbc7935
|
@aslakknutsen , should we allow user to update In current scenario of PATCH, I have made |
f6b9613
to
a95cf7c
Compare
|
Response for PATCH WI - {
"data": {
"attributes": {
"system.creator": "d8958274-f741-4558-b117-37cb2ed50d09",
"system.description": null,
"system.remote_item_id": null,
"system.state": "open",
"system.title": "cool title",
"version": 8
},
"id": "1",
"relationships": {
"assignee": {
"data": {
"id": "75743e50-641c-4ca5-a10d-9ae824f752f6",
"type": "identities"
}
},
"baseType": {
"data": {
"id": "system.userstory",
"type": "workitemtypes"
}
}
},
"type": "workitems"
},
"links": {
"self": "http://localhost:8080/api/workitems.2/1"
}
} |
|
Doesn't have to be part of this, but system.creator is a relationship as well. And technically the relationships should have a link to the target (we don't have a single User service atm, but we do have a Type endpoint) |
|
@pranavgore09 Have you looked at this part of the spec? http://jsonapi.org/format/#crud-updating-relationships |
|
@aslakknutsen yes and that introduces new endpoint of CRUD API for |
c73c00d
to
d09c26a
Compare
|
Just now I have pushed the code for |
|
@aslakknutsen IMO, as this API is working and integrated here. I keep this codebase as it is for updating WI. May be I am missing some point, but how can I manage |
d09c26a
to
83a87c5
Compare
| @@ -38,6 +38,71 @@ var UpdateWorkItemPayload = a.Type("UpdateWorkItemPayload", func() { | |||
| a.Required("type", "fields", "version") | |||
| }) | |||
|
|
|||
| // UpdateWorkItemJSONAPIPayload defines top level structure from jsonapi specs | |||
| // visit : http://jsonapi.org/format/#document-top-level | |||
| var UpdateWorkItemJSONAPIPayload = a.Type("UpdateWorkItemJSONAPIPayload", func() { | |||
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.
Do we need to export names?
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.
thanks, done in 22a20be
| }) | ||
|
|
||
| // WorkItemDataForUpdate defines how an update payload will look like | ||
| var WorkItemDataForUpdate = a.Type("WorkItemDataForUpdate", func() { |
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.
Do we need to export names?
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.
thanks, done in 22a20be
| }) | ||
|
|
||
| // WorkItemRelationships defines only `assignee` as of now. To be updated | ||
| var WorkItemRelationships = a.Type("WorkItemRelationships", func() { |
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.
Do we need to export names?
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.
thanks, done in 22a20be
{"data": {"attributes": {"version": "2", "unkown_attribute": "some value"},"id": "2","type": "workitems"}}For input like this, when Currently Any suggestions ? |
d252dd5
to
cd65348
Compare
|
Mmhh...WokItems! Fixed title ;-) |
|
Oops, it took me a while to get the difference :P thanks @tsmaeder |
Now the code is not considering ID from URL to perform any action. But we need to keep ID in URL as per specification by - http://jsonapi.org/format/#crud-updating
"relationship.BaseType" is not manadatory Do not allow to update Type of WI as of now Use existing type of WI to verify fields remove testing functions of creating user update affected test case
Added "links" in top level of response Added new method ConvertWorkItemToJSONAPI for WIcontroller Tests updated to absorb jsonapi response ToDo: Remove assignee
pass null ID for assignee relationship to remove existing assignment
Set/Unset Assignee Invalid UUID for assignee Invalid ID of WI Update only title Update only description
Use require instead of assert where needed.
754bf4e
to
e56bd7e
Compare
|
@tsmaeder hmm yes, okay but UI is already using new List method. |
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.
Tests are looking much better and cleaned up now. Please address the other minor issues.
| @@ -457,3 +522,10 @@ var RelationWorkItemData = a.Type("RelationWorkItemData", func() { | |||
| }) | |||
| a.Required("type", "id") | |||
| }) | |||
|
|
|||
| // WorkItemLinks has `self` as of now according to http://jsonapi.org/format/#fetching-resources | |||
| var WorkItemLinks = a.Type("WorkItemLinks", func() { | |||
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.
This name might be misleading because work item links are the once that I implemented in PR #421
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.
agreed, will WorkItemResourceLinksForJSONAPI sound okay ?
done in 519dbaf#diff-aa2cec7dc60fd15e23552b59d51fdbdcR527
| return nil, NewBadParameterError("version", version) | ||
| } | ||
| } else { | ||
| return nil, VersionConflictError{simpleError{"version is mandatory"}} |
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.
Please use NewVersionConflictError
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.
thanks done in 519dbaf#diff-d778d1bb58fa049a00282e997d150670R54
| return nil, VersionConflictError{simpleError{"version is mandatory"}} | ||
| } | ||
| if res.Version != version { | ||
| return nil, VersionConflictError{simpleError{"version conflict"}} |
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.
Please use NewVersionConflictError
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.
thanks done in 519dbaf#diff-d778d1bb58fa049a00282e997d150670R57
|
|
||
| rel := wi.Relationships | ||
| // TODO | ||
| // if rel.Assignee.Data == nil then remove the relationship for WI |
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.
So removing an assignee isn't possible at the moment?
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.
oops, need to remove this comment in code.
assignee is removed when ID=nil 519dbaf#diff-d778d1bb58fa049a00282e997d150670R74
|
|
||
| if err := tx.Save(&newWi).Error; err != nil { | ||
| log.Print(err.Error()) | ||
| return nil, InternalError{simpleError{err.Error()}} |
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.
Please use NewInternalError
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.
| assert.Equal(s.T(), updatedWI.Data.Attributes[models.SystemDescription], modifiedDescription) | ||
| } | ||
|
|
||
| func (s *WorkItem2Suite) TestWI2UpdateMultipleScenarios() { |
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.
Why not put each scenario in a distinct test method?
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.
Each scenario is already covered in a separate method above.
This is just series of actions taken back to back by updating version as needed.
| a.Media(workItem2) | ||
| }) | ||
| //ToDo: Error responses need modifications as per https://github.com/almighty/almighty-core/pull/421 | ||
| a.Response(d.BadRequest, func() { |
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.
Use a.Response(d.BadRequest, JSONAPIErrors) instead.
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR128
| a.Response(d.BadRequest, func() { | ||
| a.Media(d.ErrorMedia) | ||
| }) | ||
| a.Response(d.InternalServerError) |
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.
Use a.Response(d.InternalServerError, JSONAPIErrors) instead.
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR129
| a.Media(d.ErrorMedia) | ||
| }) | ||
| a.Response(d.InternalServerError) | ||
| a.Response(d.NotFound) |
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.
Use a.Response(d.NotFound, JSONAPIErrors) instead.
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR130
| }) | ||
| a.Response(d.InternalServerError) | ||
| a.Response(d.NotFound) | ||
| a.Response(d.Unauthorized) |
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.
Use a.Response(d.Unauthorized, JSONAPIErrors) instead.
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR131
de28334
to
5854216
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.
| a.Media(workItem2) | ||
| }) | ||
| //ToDo: Error responses need modifications as per https://github.com/almighty/almighty-core/pull/421 | ||
| a.Response(d.BadRequest, func() { |
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR128
| a.Response(d.BadRequest, func() { | ||
| a.Media(d.ErrorMedia) | ||
| }) | ||
| a.Response(d.InternalServerError) |
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR129
| a.Media(d.ErrorMedia) | ||
| }) | ||
| a.Response(d.InternalServerError) | ||
| a.Response(d.NotFound) |
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR130
| }) | ||
| a.Response(d.InternalServerError) | ||
| a.Response(d.NotFound) | ||
| a.Response(d.Unauthorized) |
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.
thanks done in 519dbaf#diff-5147d84da6b10c041a0bafd5935f9f4fR131
| @@ -457,3 +522,10 @@ var RelationWorkItemData = a.Type("RelationWorkItemData", func() { | |||
| }) | |||
| a.Required("type", "id") | |||
| }) | |||
|
|
|||
| // WorkItemLinks has `self` as of now according to http://jsonapi.org/format/#fetching-resources | |||
| var WorkItemLinks = a.Type("WorkItemLinks", func() { | |||
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.
agreed, will WorkItemResourceLinksForJSONAPI sound okay ?
done in 519dbaf#diff-aa2cec7dc60fd15e23552b59d51fdbdcR527
| BaseType: &app.RelationshipBaseType{ | ||
| Data: &app.BaseTypeData{ | ||
| ID: wi.Type, | ||
| Type: "workitemtypes", |
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.
Added this constant and now using it in 519dbaf#diff-d2f956a730e7c36b29bc0dceedd367bdR192
| op.Data.Relationships.Assignee = &app.RelationAssignee{ | ||
| Data: &app.AssigneeData{ | ||
| ID: &valStr, | ||
| Type: "identities", |
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.
| } | ||
| wi, err := appl.WorkItems2().Save(ctx, toSave) | ||
| if err != nil { | ||
| switch err := err.(type) { |
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.
big help, thanks. will consume that.
Done that in 519dbaf#diff-d2f956a730e7c36b29bc0dceedd367bdR232
| assert.Equal(s.T(), updatedWI.Data.Attributes[models.SystemDescription], modifiedDescription) | ||
| } | ||
|
|
||
| func (s *WorkItem2Suite) TestWI2UpdateMultipleScenarios() { |
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.
Each scenario is already covered in a separate method above.
This is just series of actions taken back to back by updating version as needed.
| return nil, NewBadParameterError("data.relationships.assignee.data.id", uuidStr) | ||
| } | ||
| wi.Attributes[SystemAssignee] = *uuidStr | ||
| // ToDO : make it a list and append |
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.
Yes I understand that,
But this will extend the scope of task to much extent, because it needs change in type of assignee in WI, then change in input, change in assignment logic, and while reading the WorkItem. Plus adding tests for the same. And then review on PR.
So most likely I will end up not doing it complete 100% (merge in master) by tomorrow.
And it is not blocker so....can that be done out of this PR ?
any suggestion @aslakknutsen ?
Using constant got "workitemtypes" Using NewInternalError instead of InternalError Using NewVersionConflictError
5854216
to
519dbaf
Compare
| @@ -524,7 +524,7 @@ var RelationWorkItemData = a.Type("RelationWorkItemData", func() { | |||
| }) | |||
|
|
|||
| // WorkItemLinks has `self` as of now according to http://jsonapi.org/format/#fetching-resources | |||
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 comment doesn't reflect the new name for the type.
Side effect fixes #466 (How to use - please see ui#348 )
WorkItem2 :- Patch the work item. (Allow partial updates & do not change WIT)
Follows
jsonapi specto define input payload for PATCH call.Following attributes are mandatory for every update request with correct value.
This payload is constructed for updating WI.
Above structure will update work item(if exists) with id 42 and set it's assignee to given UUID if it exists in system.