Skip to content

Commit

Permalink
Allow use of SOQL Query in composite request
Browse files Browse the repository at this point in the history
  • Loading branch information
nyergler committed May 3, 2023
1 parent 0fb754b commit d2e9188
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 43 deletions.
38 changes: 34 additions & 4 deletions composite/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import (
"net/http"

"github.com/namely/go-sfdc/v3"
"github.com/namely/go-sfdc/v3/composite/batch"
"github.com/namely/go-sfdc/v3/session"
"github.com/pkg/errors"
)

var (
_ Subrequester = (*GetSubrequest)(nil)
)

// Subrequester provides the composite API requests. The
// order of the array is the order in which the subrequests are
// placed in the composite body.
Expand All @@ -27,10 +32,10 @@ type Value struct {
Response []Subvalue `json:"compositeResponse"`
}

// Subvalue is the subresponses to the composite API. Using the
// referende id, one will be able to match the response with the request.
// Subvalue is the subresponses to the composite API. Using the reference id,
// one will be able to match the response with the request.
type Subvalue struct {
Body interface{} `json:"body"`
Body json.RawMessage `json:"body"`
HTTPHeaders map[string]string `json:"httpHeaders"`
HTTPStatusCode int `json:"httpStatusCode"`
ReferenceID string `json:"referenceId"`
Expand Down Expand Up @@ -120,6 +125,7 @@ func (r *Resource) Retrieve(ctx context.Context, allOrNone bool, requesters []Su
}
return value, nil
}

func (r *Resource) validateSubrequests(requesters []Subrequester) error {
for _, requester := range requesters {
if requester.URL() == "" {
Expand All @@ -128,7 +134,7 @@ func (r *Resource) validateSubrequests(requesters []Subrequester) error {
if requester.ReferenceID() == "" {
return errors.New("composite subrequest: must contain a reference id")
}
if _, has := validMethods[requester.Method()]; has == false {
if _, has := validMethods[requester.Method()]; !has {
return errors.New("composite subrequest: empty or invalid method " + requester.Method())
}
if requester.HTTPHeaders() != nil {
Expand Down Expand Up @@ -167,3 +173,27 @@ func (r *Resource) payload(allOrNone bool, requesters []Subrequester) (*bytes.Re
}
return bytes.NewReader(jsonBody), nil
}

type GetSubrequest struct {
batch.Subrequester
refID string
}

func NewGetSubrequest(refID string, req batch.Subrequester) *GetSubrequest {
return &GetSubrequest{
Subrequester: req,
refID: refID,
}
}

func (r *GetSubrequest) ReferenceID() string {
return r.refID
}

func (*GetSubrequest) HTTPHeaders() http.Header {
return nil
}

func (*GetSubrequest) Body() map[string]interface{} {
return nil
}
127 changes: 88 additions & 39 deletions composite/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package composite

import (
"context"
"io/ioutil"
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"strings"
Expand Down Expand Up @@ -58,7 +60,7 @@ func TestResource_validateSubrequests(t *testing.T) {
referenceID: "someID",
method: http.MethodGet,
httpHeaders: http.Header(map[string][]string{
"Accept-Language": []string{"en-us,"},
"Accept-Language": {"en-us,"},
}),
},
},
Expand All @@ -75,7 +77,7 @@ func TestResource_validateSubrequests(t *testing.T) {
referenceID: "someID",
method: http.MethodGet,
httpHeaders: http.Header(map[string][]string{
"Accept-Language": []string{"en-us,"},
"Accept-Language": {"en-us,"},
}),
},
},
Expand All @@ -92,7 +94,7 @@ func TestResource_validateSubrequests(t *testing.T) {
referenceID: "",
method: http.MethodGet,
httpHeaders: http.Header(map[string][]string{
"Accept-Language": []string{"en-us,"},
"Accept-Language": {"en-us,"},
}),
},
},
Expand All @@ -109,7 +111,7 @@ func TestResource_validateSubrequests(t *testing.T) {
referenceID: "someID",
method: http.MethodTrace,
httpHeaders: http.Header(map[string][]string{
"Accept-Language": []string{"en-us,"},
"Accept-Language": {"en-us,"},
}),
},
},
Expand All @@ -126,7 +128,7 @@ func TestResource_validateSubrequests(t *testing.T) {
referenceID: "someID",
method: "",
httpHeaders: http.Header(map[string][]string{
"Accept-Language": []string{"en-us,"},
"Accept-Language": {"en-us,"},
}),
},
},
Expand All @@ -143,7 +145,7 @@ func TestResource_validateSubrequests(t *testing.T) {
referenceID: "someID",
method: http.MethodGet,
httpHeaders: http.Header(map[string][]string{
"Accept": []string{"application/json"},
"Accept": {"application/json"},
}),
},
},
Expand Down Expand Up @@ -188,7 +190,7 @@ func TestResource_payload(t *testing.T) {
referenceID: "someID",
method: http.MethodGet,
httpHeaders: http.Header(map[string][]string{
"Accept-Language": []string{"en-us,"},
"Accept-Language": {"en-us,"},
}),
},
},
Expand Down Expand Up @@ -278,7 +280,7 @@ func TestResource_Retrieve(t *testing.T) {
return &http.Response{
StatusCode: 500,
Status: "Invalid URL",
Body: ioutil.NopCloser(strings.NewReader(req.URL.String())),
Body: io.NopCloser(strings.NewReader(req.URL.String())),
Header: make(http.Header),
}
}
Expand Down Expand Up @@ -345,7 +347,7 @@ func TestResource_Retrieve(t *testing.T) {
return &http.Response{
StatusCode: http.StatusOK,
Status: "Good",
Body: ioutil.NopCloser(strings.NewReader(resp)),
Body: io.NopCloser(strings.NewReader(resp)),
Header: make(http.Header),
}

Expand All @@ -366,21 +368,23 @@ func TestResource_Retrieve(t *testing.T) {
want: Value{
Response: []Subvalue{
{
Body: map[string]interface{}{
"id": "001R00000033JNuIAM",
"success": true,
"errors": make([]interface{}, 0),
},
Body: marshal(t,
map[string]interface{}{
"id": "001R00000033JNuIAM",
"success": true,
"errors": make([]interface{}, 0),
}),
HTTPHeaders: map[string]string{
"Location": "/services/data/v38.0/sobjects/Account/001R00000033JNuIAM",
},
HTTPStatusCode: 201,
ReferenceID: "NewAccount",
},
{
Body: map[string]interface{}{
"what": "all the account data",
},
Body: marshal(t,
map[string]interface{}{
"what": "all the account data",
}),
HTTPHeaders: map[string]string{
"ETag": "\"Jbjuzw7dbhaEG3fd90kJbx6A0ow=\"",
"Last-Modified": "Fri, 22 Jul 2016 20:19:37 GMT",
Expand All @@ -389,30 +393,32 @@ func TestResource_Retrieve(t *testing.T) {
ReferenceID: "NewAccountInfo",
},
{
Body: map[string]interface{}{
"id": "003R00000025REHIA2",
"success": true,
"errors": make([]interface{}, 0),
},
Body: marshal(t,
map[string]interface{}{
"id": "003R00000025REHIA2",
"success": true,
"errors": make([]interface{}, 0),
}),
HTTPHeaders: map[string]string{
"Location": "/services/data/v38.0/sobjects/Contact/003R00000025REHIA2",
},
HTTPStatusCode: 201,
ReferenceID: "NewContact",
},
{
Body: map[string]interface{}{
"attributes": map[string]interface{}{
"type": "User",
"url": "/services/data/v38.0/sobjects/User/005R0000000I90CIAS",
},
"Name": "Jane Doe",
"CompanyName": "Salesforce",
"Title": "Director",
"City": "San Francisco",
"State": "CA",
"Id": "005R0000000I90CIAS",
},
Body: marshal(t,
map[string]interface{}{
"attributes": map[string]interface{}{
"type": "User",
"url": "/services/data/v38.0/sobjects/User/005R0000000I90CIAS",
},
"Name": "Jane Doe",
"CompanyName": "Salesforce",
"Title": "Director",
"City": "San Francisco",
"State": "CA",
"Id": "005R0000000I90CIAS",
}),
HTTPHeaders: map[string]string{},
HTTPStatusCode: 200,
ReferenceID: "NewAccountOwner",
Expand Down Expand Up @@ -440,7 +446,7 @@ func TestResource_Retrieve(t *testing.T) {
return &http.Response{
StatusCode: 500,
Status: "Invalid URL",
Body: ioutil.NopCloser(strings.NewReader(req.URL.String())),
Body: io.NopCloser(strings.NewReader(req.URL.String())),
Header: make(http.Header),
}
}
Expand All @@ -455,7 +461,7 @@ func TestResource_Retrieve(t *testing.T) {
return &http.Response{
StatusCode: http.StatusBadRequest,
Status: "Bad",
Body: ioutil.NopCloser(strings.NewReader(resp)),
Body: io.NopCloser(strings.NewReader(resp)),
Header: make(http.Header),
}

Expand Down Expand Up @@ -487,9 +493,52 @@ func TestResource_Retrieve(t *testing.T) {
t.Errorf("Resource.Retrieve() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Resource.Retrieve() = %v, want %v", got, tt.want)
for i := range got.Response {
if got.Response[i].ReferenceID != tt.want.Response[i].ReferenceID {
t.Errorf("Resource.Retrieve() ReferenceID = %v, want %v", got, tt.want)
}
if got.Response[i].HTTPStatusCode != tt.want.Response[i].HTTPStatusCode {
t.Errorf("Resource.Retrieve() Status Code = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got.Response[i].HTTPHeaders, tt.want.Response[i].HTTPHeaders) {
t.Errorf("Resource.Retrieve() Headers = %v, want %v", got, tt.want)
}
if ok, err := equalJSON(got.Response[i].Body, tt.want.Response[i].Body); !ok || err != nil {
if err != nil {
fmt.Println(i)
panic(err)
}
t.Errorf("Resource.Retrieve() Body = %v, want %v", got, tt.want)
}
}
})
}
}

func marshal(t *testing.T, val any) json.RawMessage {
bytes, err := json.Marshal(val)
if err != nil {
t.Fatal(err)
}
return bytes
}

func equalJSON(s1, s2 []byte) (bool, error) {
var o1 interface{}
var o2 interface{}

var err error
if len(s1) > 0 {
err = json.Unmarshal(s1, &o1)
if err != nil {
return false, fmt.Errorf("Error mashalling string 1 :: %s", err.Error())
}
}
if len(s2) > 2 {
err = json.Unmarshal(s2, &o2)
if err != nil {
return false, fmt.Errorf("Error mashalling string 2 :: %s", err.Error())
}
}
return reflect.DeepEqual(o1, o2), nil
}

0 comments on commit d2e9188

Please sign in to comment.