// enrichLinkList includes related resources in the linkArr's "included" element
func enrichLinkList(ctx *workItemLinkContext, linkArr *app.WorkItemLinkList) error {

	// include link types
	typeDataArr, err := getTypesOfLinks(ctx, linkArr.Data)
	if err != nil {
		return errs.WithStack(err)
	}
	// Convert slice of objects to slice of interface (see https://golang.org/doc/faq#convert_slice_of_interface)
	interfaceArr := make([]interface{}, len(typeDataArr))
	for i, v := range typeDataArr {
		interfaceArr[i] = v
	}
	linkArr.Included = append(linkArr.Included, interfaceArr...)

	// include link categories
	catDataArr, err := getCategoriesOfLinkTypes(ctx, typeDataArr)
	if err != nil {
		return errs.WithStack(err)
	}
	// Convert slice of objects to slice of interface (see https://golang.org/doc/faq#convert_slice_of_interface)
	interfaceArr = make([]interface{}, len(catDataArr))
	for i, v := range catDataArr {
		interfaceArr[i] = v
	}
	linkArr.Included = append(linkArr.Included, interfaceArr...)

	// TODO(kwk): Include WIs from source and target
	workItemDataArr, err := getWorkItemsOfLinks(ctx, linkArr.Data)
	if err != nil {
		return errs.WithStack(err)
	}
	// Convert slice of objects to slice of interface (see https://golang.org/doc/faq#convert_slice_of_interface)
	interfaceArr = make([]interface{}, len(workItemDataArr))
	for i, v := range workItemDataArr {
		interfaceArr[i] = v
	}
	linkArr.Included = append(linkArr.Included, interfaceArr...)

	// TODO(kwk): Include WITs (once #559 is merged)

	// Add links to individual link data element
	for _, link := range linkArr.Data {
		selfURL := rest.AbsoluteURL(ctx.RequestData, ctx.LinkFunc(*link.ID))
		link.Links = &app.GenericLinks{
			Self: &selfURL,
		}
	}

	return nil
}
func (r *GormWorkItemLinkRepository) list(ctx context.Context, fetchFunc fetchLinksFunc) (*app.WorkItemLinkList, error) {
	rows, err := fetchFunc()
	if err != nil {
		return nil, errs.WithStack(err)
	}
	res := app.WorkItemLinkList{}
	res.Data = make([]*app.WorkItemLinkData, len(rows))
	for index, value := range rows {
		cat := ConvertLinkFromModel(value)
		res.Data[index] = cat.Data
	}
	// TODO: When adding pagination, this must not be len(rows) but
	// the overall total number of elements from all pages.
	res.Meta = &app.WorkItemLinkListMeta{
		TotalCount: len(rows),
	}
	return &res, nil
}
// validateSomeLinks validates that workItemLink1 and workItemLink2 are in the
// linkCollection and that all resources are included
func (s *workItemLinkSuite) validateSomeLinks(linkCollection *app.WorkItemLinkList, workItemLink1, workItemLink2 *app.WorkItemLinkSingle) {
	require.NotNil(s.T(), linkCollection)
	require.Nil(s.T(), linkCollection.Validate())
	// Check the number of found work item links
	require.NotNil(s.T(), linkCollection.Data)
	require.Condition(s.T(), func() bool {
		return (len(linkCollection.Data) >= 2)
	}, "At least two work item links must exist (%s and %s), but only %d exist.", *workItemLink1.Data.ID, *workItemLink2.Data.ID, len(linkCollection.Data))
	// Search for the work item types that must exist at minimum
	toBeFound := 2
	for i := 0; i < len(linkCollection.Data) && toBeFound > 0; i++ {
		actualLink := *linkCollection.Data[i]
		var expectedLink *app.WorkItemLinkData

		switch *actualLink.ID {
		case *workItemLink1.Data.ID:
			expectedLink = workItemLink1.Data
		case *workItemLink2.Data.ID:
			expectedLink = workItemLink2.Data
		}

		if expectedLink != nil {
			s.T().Log("Found work item link in collection: ", *expectedLink.ID)
			toBeFound--

			// Check JSONAPI "type"" field (should be "workitemlinks")
			require.Equal(s.T(), expectedLink.Type, actualLink.Type)

			// Check work item link type
			require.Equal(s.T(), expectedLink.Relationships.LinkType.Data.ID, actualLink.Relationships.LinkType.Data.ID)
			require.Equal(s.T(), expectedLink.Relationships.LinkType.Data.Type, actualLink.Relationships.LinkType.Data.Type)

			// Check source type
			require.Equal(s.T(), expectedLink.Relationships.Source.Data.ID, actualLink.Relationships.Source.Data.ID, "Wrong source ID for the link")
			require.Equal(s.T(), expectedLink.Relationships.Source.Data.Type, actualLink.Relationships.Source.Data.Type, "Wrong source JSONAPI type for the link")

			// Check target type
			require.Equal(s.T(), expectedLink.Relationships.Target.Data.ID, actualLink.Relationships.Target.Data.ID, "Wrong target ID for the link")
			require.Equal(s.T(), expectedLink.Relationships.Target.Data.Type, actualLink.Relationships.Target.Data.Type, "Wrong target JSONAPI type for the link")
		}
	}
	require.Exactly(s.T(), 0, toBeFound, "Not all required work item links (%s and %s) where found.", *workItemLink1.Data.ID, *workItemLink2.Data.ID)

	toBeFound = 5 // 1 x link category, 1 x link type, 3 x work items
	for i := 0; i < len(linkCollection.Included) && toBeFound > 0; i++ {
		switch v := linkCollection.Included[i].(type) {
		case *app.WorkItemLinkCategoryData:
			if *v.ID == s.userLinkCategoryID {
				s.T().Log("Found work item link category in \"included\" element: ", *v.ID)
				toBeFound--
			}
		case *app.WorkItemLinkTypeData:
			if *v.ID == s.bugBlockerLinkTypeID {
				s.T().Log("Found work item link type in \"included\" element: ", *v.ID)
				toBeFound--
			}
		case *app.WorkItem2:
			wid, err := strconv.ParseUint(*v.ID, 10, 64)
			require.Nil(s.T(), err)
			if wid == s.bug1ID || wid == s.bug2ID || wid == s.bug3ID {
				s.T().Log("Found work item in \"included\" element: ", *v.ID)
				toBeFound--
			}
		// TODO(kwk): Check for WITs (once #559 is merged)
		// case *app.WorkItemTypeData:
		default:
			s.T().Errorf("Object of unknown type included in work item link list response: %T", linkCollection.Included[i])
		}
	}
	require.Exactly(s.T(), 0, toBeFound, "Not all required included elements where found.")
}