@@ -2,6 +2,7 @@ package controllers
22
33import (
44 "errors"
5+ "fmt"
56 "net/http"
67 "strings"
78
@@ -14,11 +15,20 @@ import (
1415)
1516
1617type AllocationResponse struct {
17- Data models. Allocation `json:"data"`
18+ Data Allocation `json:"data"`
1819}
1920
2021type AllocationListResponse struct {
21- Data []models.Allocation `json:"data"`
22+ Data []Allocation `json:"data"`
23+ }
24+
25+ type Allocation struct {
26+ models.Allocation
27+ Links AllocationLinks `json:"links"`
28+ }
29+
30+ type AllocationLinks struct {
31+ Self string `json:"self" example:"https://example.com/api/v1/allocations/47"`
2232}
2333
2434// RegisterAllocationRoutes registers the routes for allocations with
@@ -44,10 +54,7 @@ func RegisterAllocationRoutes(r *gin.RouterGroup) {
4454// @Description Returns an empty response with the HTTP Header "allow" set to the allowed HTTP verbs
4555// @Tags Allocations
4656// @Success 204
47- // @Param budgetId path uint64 true "ID of the budget"
48- // @Param categoryId path uint64 true "ID of the category"
49- // @Param envelopeId path uint64 true "ID of the envelope"
50- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/allocations [options]
57+ // @Router /v1/allocations [options]
5158func OptionsAllocationList (c * gin.Context ) {
5259 httputil .OptionsGetPost (c )
5360}
@@ -56,11 +63,8 @@ func OptionsAllocationList(c *gin.Context) {
5663// @Description Returns an empty response with the HTTP Header "allow" set to the allowed HTTP verbs
5764// @Tags Allocations
5865// @Success 204
59- // @Param budgetId path uint64 true "ID of the budget"
60- // @Param categoryId path uint64 true "ID of the category"
61- // @Param envelopeId path uint64 true "ID of the envelope"
6266// @Param allocationId path uint64 true "ID of the allocation"
63- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/ allocations/{allocationId} [options]
67+ // @Router /v1/allocations/{allocationId} [options]
6468func OptionsAllocationDetail (c * gin.Context ) {
6569 httputil .OptionsGetPatchDelete (c )
6670}
@@ -73,24 +77,22 @@ func OptionsAllocationDetail(c *gin.Context) {
7377// @Failure 400 {object} httputil.HTTPError
7478// @Failure 404
7579// @Failure 500 {object} httputil.HTTPError
76- // @Param budgetId path uint64 true "ID of the budget"
77- // @Param categoryId path uint64 true "ID of the category"
78- // @Param envelopeId path uint64 true "ID of the envelope"
7980// @Param allocation body models.AllocationCreate true "Allocation"
80- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/ allocations [post]
81+ // @Router /v1/allocations [post]
8182func CreateAllocation (c * gin.Context ) {
82- var data models.Allocation
83+ var allocation models.Allocation
8384
84- err := httputil .BindData (c , & data )
85+ err := httputil .BindData (c , & allocation )
8586 if err != nil {
8687 return
8788 }
8889
89- data . EnvelopeID , err = httputil . ParseID (c , "envelopeId" )
90+ _ , err = getEnvelopeResource (c , allocation . EnvelopeID )
9091 if err != nil {
9192 return
9293 }
93- result := models .DB .Create (& data )
94+
95+ result := models .DB .Create (& allocation )
9496
9597 if result .Error != nil {
9698 // By default, we assume a server error
@@ -115,7 +117,8 @@ func CreateAllocation(c *gin.Context) {
115117 return
116118 }
117119
118- c .JSON (http .StatusCreated , AllocationResponse {Data : data })
120+ allocationObject , _ := getAllocationObject (c , allocation .ID )
121+ c .JSON (http .StatusCreated , AllocationResponse {Data : allocationObject })
119122}
120123
121124// @Summary Get all allocations for an envelope
@@ -125,27 +128,24 @@ func CreateAllocation(c *gin.Context) {
125128// @Success 200 {object} AllocationListResponse
126129// @Failure 400 {object} httputil.HTTPError
127130// @Failure 404
128- // @Failure 500 {object} httputil.HTTPError
129- // @Param budgetId path uint64 true "ID of the budget"
130- // @Param categoryId path uint64 true "ID of the category"
131- // @Param envelopeId path uint64 true "ID of the envelope"
132- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/allocations [get]
131+ // @Failure 500 {object} httputil.HTTPError
132+ // @Router /v1/allocations [get]
133133func GetAllocations (c * gin.Context ) {
134134 var allocations []models.Allocation
135135
136- // Check if the envelope exists
137- envelope , err := getEnvelopeResource (c )
138- if err != nil {
139- return
140- }
136+ models .DB .Find (& allocations )
141137
142- models .DB .Where (& models.Allocation {
143- AllocationCreate : models.AllocationCreate {
144- EnvelopeID : envelope .ID ,
145- },
146- }).Find (& allocations )
138+ // When there are no resources, we want an empty list, not null
139+ // Therefore, we use make to create a slice with zero elements
140+ // which will be marshalled to an empty JSON array
141+ allocationObjects := make ([]Allocation , 0 )
147142
148- c .JSON (http .StatusOK , AllocationListResponse {Data : allocations })
143+ for _ , allocation := range allocations {
144+ o , _ := getAllocationObject (c , allocation .ID )
145+ allocationObjects = append (allocationObjects , o )
146+ }
147+
148+ c .JSON (http .StatusOK , AllocationListResponse {Data : allocationObjects })
149149}
150150
151151// @Summary Get allocation
@@ -156,18 +156,20 @@ func GetAllocations(c *gin.Context) {
156156// @Failure 400 {object} httputil.HTTPError
157157// @Failure 404
158158// @Failure 500 {object} httputil.HTTPError
159- // @Param budgetId path uint64 true "ID of the budget"
160- // @Param categoryId path uint64 true "ID of the category"
161- // @Param envelopeId path uint64 true "ID of the envelope"
162159// @Param allocationId path uint64 true "ID of the allocation"
163- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/ allocations/{allocationId} [get]
160+ // @Router /v1/allocations/{allocationId} [get]
164161func GetAllocation (c * gin.Context ) {
165- allocation , err := getAllocationResource (c )
162+ id , err := httputil .ParseID (c , "allocationId" )
163+ if err != nil {
164+ return
165+ }
166+
167+ allocationObject , err := getAllocationObject (c , id )
166168 if err != nil {
167169 return
168170 }
169171
170- c .JSON (http .StatusOK , AllocationResponse {Data : allocation })
172+ c .JSON (http .StatusOK , AllocationResponse {Data : allocationObject })
171173}
172174
173175// @Summary Update an allocation
@@ -179,14 +181,16 @@ func GetAllocation(c *gin.Context) {
179181// @Failure 400 {object} httputil.HTTPError
180182// @Failure 404
181183// @Failure 500 {object} httputil.HTTPError
182- // @Param budgetId path uint64 true "ID of the budget"
183- // @Param categoryId path uint64 true "ID of the category"
184- // @Param envelopeId path uint64 true "ID of the envelope"
185184// @Param allocationId path uint64 true "ID of the allocation"
186185// @Param allocation body models.AllocationCreate true "Allocation"
187- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/ allocations/{allocationId} [patch]
186+ // @Router /v1/allocations/{allocationId} [patch]
188187func UpdateAllocation (c * gin.Context ) {
189- allocation , err := getAllocationResource (c )
188+ id , err := httputil .ParseID (c , "allocationId" )
189+ if err != nil {
190+ return
191+ }
192+
193+ allocation , err := getAllocationResource (c , id )
190194 if err != nil {
191195 return
192196 }
@@ -197,7 +201,9 @@ func UpdateAllocation(c *gin.Context) {
197201 }
198202
199203 models .DB .Model (& allocation ).Updates (data )
200- c .JSON (http .StatusOK , AllocationResponse {Data : allocation })
204+ allocationObject , _ := getAllocationObject (c , allocation .ID )
205+
206+ c .JSON (http .StatusOK , AllocationResponse {Data : allocationObject })
201207}
202208
203209// @Summary Delete an allocation
@@ -207,13 +213,15 @@ func UpdateAllocation(c *gin.Context) {
207213// @Failure 400 {object} httputil.HTTPError
208214// @Failure 404
209215// @Failure 500 {object} httputil.HTTPError
210- // @Param budgetId path uint64 true "ID of the budget"
211- // @Param categoryId path uint64 true "ID of the category"
212- // @Param envelopeId path uint64 true "ID of the envelope"
213216// @Param allocationId path uint64 true "ID of the allocation"
214- // @Router /v1/budgets/{budgetId}/categories/{categoryId}/envelopes/{envelopeId}/ allocations/{allocationId} [delete]
217+ // @Router /v1/allocations/{allocationId} [delete]
215218func DeleteAllocation (c * gin.Context ) {
216- allocation , err := getAllocationResource (c )
219+ id , err := httputil .ParseID (c , "allocationId" )
220+ if err != nil {
221+ return
222+ }
223+
224+ allocation , err := getAllocationResource (c , id )
217225 if err != nil {
218226 return
219227 }
@@ -224,25 +232,12 @@ func DeleteAllocation(c *gin.Context) {
224232}
225233
226234// getAllocationResource verifies that the request URI is valid for the transaction and returns it.
227- func getAllocationResource (c * gin.Context ) (models.Allocation , error ) {
235+ func getAllocationResource (c * gin.Context , id uint64 ) (models.Allocation , error ) {
228236 var allocation models.Allocation
229237
230- envelope , err := getEnvelopeResource (c )
231- if err != nil {
232- return models.Allocation {}, err
233- }
234-
235- allocationID , err := httputil .ParseID (c , "allocationId" )
236- if err != nil {
237- return models.Allocation {}, err
238- }
239-
240- err = models .DB .First (& allocation , & models.Allocation {
241- AllocationCreate : models.AllocationCreate {
242- EnvelopeID : envelope .ID ,
243- },
238+ err := models .DB .First (& allocation , & models.Allocation {
244239 Model : models.Model {
245- ID : allocationID ,
240+ ID : id ,
246241 },
247242 }).Error
248243 if err != nil {
@@ -252,3 +247,27 @@ func getAllocationResource(c *gin.Context) (models.Allocation, error) {
252247
253248 return allocation , nil
254249}
250+
251+ func getAllocationObject (c * gin.Context , id uint64 ) (Allocation , error ) {
252+ resource , err := getAllocationResource (c , id )
253+ if err != nil {
254+ return Allocation {}, err
255+ }
256+
257+ return Allocation {
258+ resource ,
259+ getAllocationLinks (c , id ),
260+ }, nil
261+ }
262+
263+ // getAllocationLinks returns a BudgetLinks struct.
264+ //
265+ // This function is only needed for getAllocationObject as we cannot create an instance of Allocation
266+ // with mixed named and unnamed parameters.
267+ func getAllocationLinks (c * gin.Context , id uint64 ) AllocationLinks {
268+ url := httputil .RequestPathV1 (c ) + fmt .Sprintf ("/allocations/%d" , id )
269+
270+ return AllocationLinks {
271+ Self : url ,
272+ }
273+ }
0 commit comments