Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Commit

Permalink
Implement /api/workitem/:ID/relationships/links (#525)
Browse files Browse the repository at this point in the history
This change will introduce the endpoint `/api/workitems/:ID/relationships/links` for work item links that lives under the work item endpoint and automatically filters work item links associated with the work item on the backend. You can also use this endpoint as you would use the `/api/workitemlinks/` endpoint.

Also, this change will introduce the `included` top-level JSONAPI element for responses of `/api/workitemlinks/` that return work item links or arrays of work item links. Currently this `included` element will contain only objects of type work item link type.

Currently the UI has to do the filtering of links on a particular work item. This should be no longer needed.

== Checks

In the WorkItemRelationshipsLinksController we perform the following
checks for the listed actions.

===  Create action
* We check that the current work item (:id) does exist.
* Check that the source ID of the link is the same as the current work
  item ID (:id).
* If no source is specified we pre-fill the source field of the payload
  with the current work item ID from the URL. This is for convenience.

=== Delete action
* We check that the work item (:linkid) does exist.
* Check that the source ID of the link to be deleted is the same as the
  current work item ID (:id).

=== List action
* No checks are done

=== Show action
* We check that the work item (:linkid) does exist.
* Check that the source ID or target ID of the link to be shown is the
  same as the current work item ID (:id).

=== Update action
* We check that the work item (:linkid) does exist.
* Check that the source ID of the link to be updated is the same as the
  current work item ID (:id).
* Check that the source ID of the update payload is the same as the
  current work item ID (:id).

Related to #307
  • Loading branch information
kwk authored and aslakknutsen committed Dec 9, 2016
1 parent 90158fd commit 3d7da8c
Show file tree
Hide file tree
Showing 12 changed files with 1,078 additions and 544 deletions.
2 changes: 2 additions & 0 deletions auth-utility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (
endpointWorkItemLinkCategories = "/api/workitemlinkcategories"
endpointWorkItemLinkTypes = "/api/workitemlinktypes"
endpointWorkItemLinks = "/api/workitemlinks"

endpointWorkItemRelationshipsLinks = endpointWorkItems + "/%s/relationships/links"
)

// testSecureAPI defines how a Test object is.
Expand Down
4 changes: 4 additions & 0 deletions design/media_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,12 @@ var WorkItemLink = a.MediaType("application/vnd.work-item-link+json", func() {
a.Description(`Defines a connection between two work items`)
a.Attributes(func() {
a.Attribute("data", WorkItemLinkData)
a.Attribute("included", a.ArrayOf(WorkItemLinkTypeData))
a.Required("data")
})
a.View("default", func() {
a.Attribute("data")
a.Attribute("included")
a.Required("data")
})
})
Expand All @@ -379,10 +381,12 @@ var WorkItemLinkArray = a.MediaType("application/vnd.work-item-link-array+json",
a.Attributes(func() {
a.Attribute("meta", WorkItemLinkArrayMeta)
a.Attribute("data", a.ArrayOf(WorkItemLinkData))
a.Attribute("included", a.ArrayOf(WorkItemLinkTypeData))
a.Required("data")
})
a.View("default", func() {
a.Attribute("data")
a.Attribute("included")
a.Attribute("meta")
a.Required("data")
})
Expand Down
172 changes: 100 additions & 72 deletions design/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ var _ = a.Resource("work-item-link-type", func() {
a.Routing(
a.GET("/:id"),
)
a.Description("Retrieve work item link type (as JSONAPI) for the given ID.")
a.Description("Retrieve work item link type (as JSONAPI) for the given link ID.")
a.Params(func() {
a.Param("id", d.String, "ID of the work item link type")
})
Expand Down Expand Up @@ -612,82 +612,110 @@ var _ = a.Resource("work-item-link-type", func() {

var _ = a.Resource("work-item-link", func() {
a.BasePath("/workitemlinks")
a.Action("show", showWorkItemLink)
a.Action("list", listWorkItemLinks)
a.Action("create", createWorkItemLink)
a.Action("delete", deleteWorkItemLink)
a.Action("update", updateWorkItemLink)
})

a.Action("show", func() {
a.Routing(
a.GET("/:id"),
)
a.Description("Retrieve work item link (as JSONAPI) for the given ID.")
a.Params(func() {
a.Param("id", d.String, "ID of the work item link")
})
a.Response(d.OK, func() {
a.Media(WorkItemLink)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
})

var _ = a.Resource("work-item-relationships-links", func() {
a.BasePath("/relationships/links")
a.Parent("workitem")
a.Action("show", showWorkItemLink)
a.Action("delete", deleteWorkItemLink)
a.Action("update", updateWorkItemLink)
a.Action("list", func() {
a.Routing(
a.GET(""),
)
a.Description("List work item links.")
a.Response(d.OK, func() {
a.Media(WorkItemLinkArray)
listWorkItemLinks()
a.Description("List work item links associated with the given work item (either as source or as target work item).")
a.Response(d.NotFound, JSONAPIErrors, func() {
a.Description("This error arises when the given work item does not exist.")
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
})

a.Action("create", func() {
a.Security("jwt")
a.Routing(
a.POST(""),
)
a.Description("Create a work item link")
a.Payload(CreateWorkItemLinkPayload)
a.Response(d.Created, "/workitemlinks/.*", func() {
a.Media(WorkItemLink)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
})

a.Action("delete", func() {
a.Security("jwt")
a.Routing(
a.DELETE("/:id"),
)
a.Description("Delete work item link with given id.")
a.Params(func() {
a.Param("id", d.String, "id")
})
a.Response(d.OK)
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
})

a.Action("update", func() {
a.Security("jwt")
a.Routing(
a.PATCH("/:id"),
)
a.Description("Update the given work item link with given id.")
a.Params(func() {
a.Param("id", d.String, "id")
createWorkItemLink()
a.Response(d.NotFound, JSONAPIErrors, func() {
a.Description("This error arises when the given work item does not exist.")
})
a.Payload(UpdateWorkItemLinkPayload)
a.Response(d.OK, func() {
a.Media(WorkItemLink)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
})
})

// listWorkItemLinks defines the list action for endpoints that return an array
// of work item links.
func listWorkItemLinks() {
a.Description("Retrieve work item link (as JSONAPI) for the given link ID.")
a.Routing(
a.GET(""),
)
a.Response(d.OK, func() {
a.Media(WorkItemLinkArray)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
}

func showWorkItemLink() {
a.Description("Retrieve work item link (as JSONAPI) for the given link ID.")
a.Routing(
a.GET("/:linkId"),
)
a.Params(func() {
a.Param("linkId", d.String, "ID of the work item link to show")
})
a.Response(d.OK, func() {
a.Media(WorkItemLink)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
}

func createWorkItemLink() {
a.Description("Create a work item link")
a.Security("jwt")
a.Routing(
a.POST(""),
)
a.Payload(CreateWorkItemLinkPayload)
a.Response(d.Created, "/workitemlinks/.*", func() {
a.Media(WorkItemLink)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
}

func deleteWorkItemLink() {
a.Description("Delete work item link with given id.")
a.Security("jwt")
a.Routing(
a.DELETE("/:linkId"),
)
a.Params(func() {
a.Param("linkId", d.String, "ID of the work item link to be deleted")
})
a.Response(d.OK)
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
}

func updateWorkItemLink() {
a.Description("Update the given work item link with given id.")
a.Security("jwt")
a.Routing(
a.PATCH("/:linkId"),
)
a.Params(func() {
a.Param("linkId", d.String, "ID of the work item link to be updated")
})
a.Payload(UpdateWorkItemLinkPayload)
a.Response(d.OK, func() {
a.Media(WorkItemLink)
})
a.Response(d.BadRequest, JSONAPIErrors)
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
}

0 comments on commit 3d7da8c

Please sign in to comment.