resolveCustomField resolves the @custom childField by making an external HTTP request and then updates the fastJson tree with results of that HTTP request. It accepts the following arguments: - childField: the @custom field which needs to be resolved - parentNodeHeads: a list of head pointers to the
(childField gqlSchema.Field, parentNodeHeads []fastJsonNode, wg *sync.WaitGroup)
| 735 | // https://medium.com/@_orcaman/when-too-much-concurrency-slows-you-down-golang-9c144ca305a |
| 736 | // - worry about path in errors and how to deal with them, specially during completion step |
| 737 | func (genc *graphQLEncoder) resolveCustomField(childField gqlSchema.Field, |
| 738 | parentNodeHeads []fastJsonNode, wg *sync.WaitGroup) { |
| 739 | defer wg.Done() // signal when this goroutine finishes execution |
| 740 | |
| 741 | fconf, err := childField.CustomHTTPConfig() |
| 742 | if err != nil { |
| 743 | genc.errCh <- x.GqlErrorList{childField.GqlErrorf(nil, "%s", err.Error())} |
| 744 | return |
| 745 | } |
| 746 | // for resolving a custom field, we need to carry out following steps: |
| 747 | // 1: Find the requiredFields data for uniqueParents from all the parentNodes |
| 748 | // 2. Construct correct URL and body using that data |
| 749 | // 3. Make the request to external HTTP endpoint using the URL and body |
| 750 | // 4. Decode the HTTP response |
| 751 | // 5. Run GraphQL completion on the decoded HTTP response |
| 752 | // 6. Create fastJson nodes which contain the completion result for this custom field for |
| 753 | // all the duplicate parents and |
| 754 | // 7. Update the fastJson tree with those fastJson nodes |
| 755 | |
| 756 | var parentNodeHeadAttr uint16 |
| 757 | if len(parentNodeHeads) > 0 { |
| 758 | parentNodeHeadAttr = genc.getAttr(parentNodeHeads[0]) |
| 759 | } |
| 760 | isGraphqlReq := fconf.RemoteGqlQueryName != "" |
| 761 | requiredFields := childField.CustomRequiredFields() |
| 762 | |
| 763 | // we need to find the ID or @id field from requiredFields as we want to make HTTP requests |
| 764 | // only for unique parent nodes. That means, we send/receive less data over the network, |
| 765 | // and thus minimize the network latency as much as possible. |
| 766 | idFieldName := "" |
| 767 | idFieldValue := "" |
| 768 | for _, fieldDef := range requiredFields { |
| 769 | if fieldDef.IsID() || fieldDef.HasIDDirective() { |
| 770 | idFieldName = fieldDef.Name() |
| 771 | break |
| 772 | } |
| 773 | } |
| 774 | if idFieldName == "" { |
| 775 | // This should not happen as we only allow custom fields which either use ID field or a |
| 776 | // field with @id directive. |
| 777 | genc.errCh <- x.GqlErrorList{childField.GqlErrorf(nil, |
| 778 | "unable to find a required field with type ID! or @id directive for @custom field %s.", |
| 779 | childField.Name())} |
| 780 | return |
| 781 | } |
| 782 | |
| 783 | // we don't know the number of unique parents in advance, |
| 784 | // so can't allocate this list with a pre-defined size |
| 785 | var uniqueParents []interface{} |
| 786 | // uniqueParentIdxToIdFieldVal stores the idFieldValue for each unique rfData |
| 787 | var uniqueParentIdxToIdFieldVal []string |
| 788 | // parentNodes is a map from idFieldValue to all the parentNodes for that idFieldValue. |
| 789 | parentNodes := make(map[string][]fastJsonNode) |
| 790 | |
| 791 | // Step-1: Find the requiredFields data for uniqueParents from all the parentNodes |
| 792 | for _, parentNodeHead := range parentNodeHeads { |
| 793 | // iterate over all the siblings of this parentNodeHead which have the same attr as this |
| 794 | for parentNode := parentNodeHead; parentNode != nil && genc.getAttr( |
no test coverage detected