// CreateCommentsRelationLinks returns a RelationGeneric object representing the links for a workitem to comment relation func CreateCommentsRelationLinks(request *goa.RequestData, wi *app.WorkItem) *app.GenericLinks { commentsSelf := rest.AbsoluteURL(request, app.WorkitemHref(wi.ID)) + "/relationships/comments" commentsRelated := rest.AbsoluteURL(request, app.WorkitemHref(wi.ID)) + "/comments" return &app.GenericLinks{ Self: &commentsSelf, Related: &commentsRelated, } }
// ConvertIteration converts between internal and external REST representation func ConvertIteration(request *goa.RequestData, itr *iteration.Iteration, additional ...IterationConvertFunc) *app.Iteration { iterationType := iteration.APIStringTypeIteration spaceType := "spaces" spaceID := itr.SpaceID.String() selfURL := rest.AbsoluteURL(request, app.IterationHref(itr.ID)) spaceSelfURL := rest.AbsoluteURL(request, app.SpaceHref(spaceID)) workitemsRelatedURL := rest.AbsoluteURL(request, app.WorkitemHref("?filter[iteration]="+itr.ID.String())) i := &app.Iteration{ Type: iterationType, ID: &itr.ID, Attributes: &app.IterationAttributes{ Name: &itr.Name, StartAt: itr.StartAt, EndAt: itr.EndAt, Description: itr.Description, State: &itr.State, }, Relationships: &app.IterationRelations{ Space: &app.RelationGeneric{ Data: &app.GenericData{ Type: &spaceType, ID: &spaceID, }, Links: &app.GenericLinks{ Self: &spaceSelfURL, }, }, Workitems: &app.RelationGeneric{ Links: &app.GenericLinks{ Related: &workitemsRelatedURL, }, }, }, Links: &app.GenericLinks{ Self: &selfURL, }, } if itr.ParentID != uuid.Nil { parentSelfURL := rest.AbsoluteURL(request, app.IterationHref(itr.ParentID)) parentID := itr.ParentID.String() i.Relationships.Parent = &app.RelationGeneric{ Data: &app.GenericData{ Type: &iterationType, ID: &parentID, }, Links: &app.GenericLinks{ Self: &parentSelfURL, }, } } for _, add := range additional { add(request, itr, i) } return i }
// Create does POST workitem func (c *WorkitemController) Create(ctx *app.CreateWorkitemContext) error { currentUser, err := login.ContextIdentity(ctx) if err != nil { jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized(err.Error())) return ctx.Unauthorized(jerrors) } var wit *string if ctx.Payload.Data != nil && ctx.Payload.Data.Relationships != nil && ctx.Payload.Data.Relationships.BaseType != nil && ctx.Payload.Data.Relationships.BaseType.Data != nil { wit = &ctx.Payload.Data.Relationships.BaseType.Data.ID } if wit == nil { // TODO Figure out path source etc. Should be a required relation return jsonapi.JSONErrorResponse(ctx, errors.NewBadParameterError("Data.Relationships.BaseType.Data.ID", err)) } wi := app.WorkItem{ Fields: make(map[string]interface{}), } return application.Transactional(c.db, func(appl application.Application) error { err := ConvertJSONAPIToWorkItem(appl, *ctx.Payload.Data, &wi) if err != nil { return jsonapi.JSONErrorResponse(ctx, errs.Wrap(err, fmt.Sprintf("Error creating work item"))) } wi, err := appl.WorkItems().Create(ctx, *wit, wi.Fields, currentUser) if err != nil { return jsonapi.JSONErrorResponse(ctx, errs.Wrap(err, fmt.Sprintf("Error creating work item"))) } wi2 := ConvertWorkItem(ctx.RequestData, wi) resp := &app.WorkItem2Single{ Data: wi2, Links: &app.WorkItemLinks{ Self: buildAbsoluteURL(ctx.RequestData), }, } ctx.ResponseData.Header().Set("Location", app.WorkitemHref(wi2.ID)) return ctx.Created(resp) }) }
// ConvertWorkItem is responsible for converting given WorkItem model object into a // response resource object by jsonapi.org specifications func ConvertWorkItem(request *goa.RequestData, wi *app.WorkItem, additional ...WorkItemConvertFunc) *app.WorkItem2 { // construct default values from input WI selfURL := rest.AbsoluteURL(request, app.WorkitemHref(wi.ID)) sourceLinkTypesURL := rest.AbsoluteURL(request, app.WorkitemtypeHref(wi.Type)+sourceLinkTypesRouteEnd) targetLinkTypesURL := rest.AbsoluteURL(request, app.WorkitemtypeHref(wi.Type)+targetLinkTypesRouteEnd) op := &app.WorkItem2{ ID: &wi.ID, Type: APIStringTypeWorkItem, Attributes: map[string]interface{}{ "version": wi.Version, }, Relationships: &app.WorkItemRelationships{ BaseType: &app.RelationBaseType{ Data: &app.BaseTypeData{ ID: wi.Type, Type: APIStringTypeWorkItemType, }, }, }, Links: &app.GenericLinksForWorkItem{ Self: &selfURL, SourceLinkTypes: &sourceLinkTypesURL, TargetLinkTypes: &targetLinkTypesURL, }, } // Move fields into Relationships or Attributes as needed // TODO: Loop based on WorKItemType and match against Field.Type instead of directly to field value for name, val := range wi.Fields { switch name { case workitem.SystemAssignees: if val != nil { valArr := val.([]interface{}) op.Relationships.Assignees = &app.RelationGenericList{ Data: ConvertUsersSimple(request, valArr), } } case workitem.SystemCreator: if val != nil { valStr := val.(string) op.Relationships.Creator = &app.RelationGeneric{ Data: ConvertUserSimple(request, valStr), } } case workitem.SystemIteration: if val != nil { valStr := val.(string) op.Relationships.Iteration = &app.RelationGeneric{ Data: ConvertIterationSimple(request, valStr), } } case workitem.SystemArea: if val != nil { valStr := val.(string) op.Relationships.Area = &app.RelationGeneric{ Data: ConvertAreaSimple(request, valStr), } } case workitem.SystemTitle: // 'HTML escape' the title to prevent script injection op.Attributes[name] = html.EscapeString(val.(string)) case workitem.SystemDescription: description := rendering.NewMarkupContentFromValue(val) if description != nil { op.Attributes[name] = (*description).Content op.Attributes[workitem.SystemDescriptionMarkup] = (*description).Markup // let's include the rendered description while 'HTML escaping' it to prevent script injection op.Attributes[workitem.SystemDescriptionRendered] = rendering.RenderMarkupToHTML(html.EscapeString((*description).Content), (*description).Markup) } default: op.Attributes[name] = val } } if op.Relationships.Assignees == nil { op.Relationships.Assignees = &app.RelationGenericList{Data: nil} } if op.Relationships.Iteration == nil { op.Relationships.Iteration = &app.RelationGeneric{Data: nil} } if op.Relationships.Area == nil { op.Relationships.Area = &app.RelationGeneric{Data: nil} } // Always include Comments Link, but optionally use WorkItemIncludeCommentsAndTotal WorkItemIncludeComments(request, wi, op) for _, add := range additional { add(request, wi, op) } return op }