(field gqlSchema.Field, n fastJsonNode)
| 607 | } |
| 608 | |
| 609 | func (genc *graphQLEncoder) processCustomFields(field gqlSchema.Field, n fastJsonNode) { |
| 610 | if field.HasCustomHTTPChild() { |
| 611 | // initially, create attr ids for all the descendents of this field, |
| 612 | // so that they don't result in race-conditions later |
| 613 | genc.initChildAttrId(field) |
| 614 | // TODO(abhimanyu): |
| 615 | // * benchmark the approach of using channels vs mutex to update the fastJson tree. |
| 616 | // * benchmark and find how much load should be put on HttpClient concurrently. |
| 617 | // * benchmark and find a default buffer capacity for these channels |
| 618 | genc.errCh = make(chan x.GqlErrorList, 3) |
| 619 | genc.customFieldResultCh = make(chan customFieldResult, 3) |
| 620 | // initialize WaitGroup for the error and result channel goroutines |
| 621 | wg := &sync.WaitGroup{} |
| 622 | wg.Add(2) |
| 623 | // keep collecting errors arising from custom field resolution until channel is closed |
| 624 | go func() { |
| 625 | for errs := range genc.errCh { |
| 626 | genc.errs = append(genc.errs, errs...) |
| 627 | } |
| 628 | wg.Done() |
| 629 | }() |
| 630 | // keep updating the fastJson tree as long as we get updates from the channel. |
| 631 | // This is the step-7 of *genc.resolveCustomField() |
| 632 | go func() { |
| 633 | // this would add the custom fastJson nodes in an arbitrary order. So, they may not |
| 634 | // be linked in the order the custom fields are present in selection set. |
| 635 | // i.e., while encoding the GraphQL response, we will have to do one of these: |
| 636 | // * a linear search to find the correct fastJson node for a custom field, or |
| 637 | // * first fix the order of custom fastJson nodes and then continue the encoding, or |
| 638 | // * create a map from custom fastJson node attr to the custom fastJson node, |
| 639 | // so that whenever a custom field is encountered in the selection set, |
| 640 | // just use the map to find out the fastJson node for that field. |
| 641 | // The last option seems better. |
| 642 | |
| 643 | // the results slice keeps all the customFieldResults in memory as is, until all the |
| 644 | // custom fields aren't resolved. Once the channel is closed, it would update the |
| 645 | // fastJson tree serially, so that there are no race conditions. |
| 646 | results := make([]customFieldResult, 0) |
| 647 | for res := range genc.customFieldResultCh { |
| 648 | results = append(results, res) |
| 649 | } |
| 650 | for _, res := range results { |
| 651 | childAttr := genc.idForAttr(res.childField.DgraphAlias()) |
| 652 | for _, parent := range res.parents { |
| 653 | childNode, err := genc.makeCustomNode(childAttr, res.childVal) |
| 654 | if err != nil { |
| 655 | genc.errCh <- x.GqlErrorList{res.childField.GqlErrorf(nil, "%s", err.Error())} |
| 656 | continue |
| 657 | } |
| 658 | childNode.next = parent.child |
| 659 | parent.child = childNode |
| 660 | } |
| 661 | } |
| 662 | wg.Done() |
| 663 | }() |
| 664 | // extract the representations for Apollo _entities query and store them in GraphQL encoder |
| 665 | if q, ok := field.(gqlSchema.Query); ok && q.QueryType() == gqlSchema.EntitiesQuery { |
| 666 | // ignore the error here, as that should have been taken care of during query rewriting |
no test coverage detected