diff --git a/README.md b/README.md index 4f7e047..55dd2ef 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,13 @@ your integration. - `identity.resolve` - `identity.delete` +- _[Permission](https://platform.fullcontact.com/docs/apis/permission/introduction)_ + - `permission.create` + - `permission.delete` + - `permission.find` + - `permission.current` + - `permission.verify` + ## Providing Authentication to FullContact Client FullContact client uses ```CredentialsProvider``` interface for Authentication. Different ways to provide authentication: @@ -165,6 +172,7 @@ such as: - `PersonId`: _string_ - `LiNonId`: _string_ - `PartnerId`: _string_ +- `Placekey`: _string_ ```go @@ -300,6 +308,7 @@ such as: - `PersonId`: _string_ - `LiNonId`: _string_ - `PartnerId`: _string_ +- `Placekey`: _string_ ```go resolveRequest, err := fc.NewResolveRequest( @@ -441,3 +450,159 @@ if resp.IsSuccessful { } ``` +## Permission +[Permission API Reference](https://platform.fullcontact.com/docs/apis/permission/introduction) +- `permission.create` +- `permission.delete` +- `permission.find` +- `permission.current` +- `permission.verify` + +#### Permission Request +Permission uses the following type of parameters for it's requests + +`PermissionRequest` for +- `permission.create` +- `permission.verify` + +and `MultifieldRequest` for +- `permission.delete` +- `permission.find` +- `permission.current` + +You can build a Permission Request by using `NewPermissionRequest` +and setting different input parameters that you have. + +All Permission Api requires `MultifieldRequest` requests, which can be constructed by using `NewMultifieldRequest` and following are it's parameters. + +- `Emails`: _[]string_ +- `Phones`: _[]string_ +- `Location`: _*Location_ + - `AddressLine1`: _string_ + - `AddressLine2`: _string_ + - `City`: _string_ + - `Region`: _string_ + - `RegionCode`: _string_ + - `PostalCode`: _string_ +- `Name`: _*PersonName_ + - `Full`: _string_ + - `Given`: _string_ + - `Family`: _string_ +- `Profiles`: _[]*Profile_ + - `Service`: _string_ + - `Username`: _string_ + - `Userid`: _string_ + - `Url`: _string_ +- `Maids`: _[]string_ +- `RecordId`: _string_ +- `PersonId`: _string_ +- `LiNonId`: _string_ +- `PartnerId`: _string_ +- `Placekey`: _string_ + +```go +multifieldRequest, err := fc.NewMultifieldRequest( + fc.WithEmailForMultifieldRequest("bart@fullcontact.com")) +permissionRequest, err := fc.NewPermissionRequest( + fc.WithMultifieldRequestForPermission(multifieldRequest)) +``` + +### Permission Request +All permission methods returns a `channel` of type `APIResponse` from which you can get corresponding response classes. + +The following are the corresponding response classes +- `PermissionCreatedResponse` - permission.create +- `PermissionDeleteResponse` - permission.delete +- `PermissionVerifyResponse` - permission.verify +- `PermissionFindResponse` - permission.find +- `PermissionCurrentResponse` - permission.current + +`PermissionCreate` and `PermissionVerify` requires a `ResolveRequest` as parameter while the rest requires `MultifieldRequest` as parameter + +### Permission Create + +#### Parameters: +Supported fields in query: +- `query`: MultifieldRequest - [required] +- `consentPurposes`: List[ConsentPurposes] - [required] +- `locale`: string +- `ipAddress`: string +- `language`: string +- `collectionMethod`: string - [required] +- `collectionLocation`: string - [required] +- `policyUrl`: string - [required] +- `termsService`: string - [required] +- `tcf`: string +- `timestamp`: int + +#### Returns: +class: `PermissionCreateResponse`. A basic API response with response code as 202 if successful. + +### Permission Verify +#### Parameters: +Supported fields in query: +- `query`: MultifieldRequest - [required] +- `purposeId`: int - [required] +- `channel`: string - [required] + +#### Returns: +class: `PermissionVerifyResponse` with following fields. + +- `ttl`: string +- `enabled`: bool +- `channel`: string +- `purposeId`: int +- `purposeName`: string +- `timestamp`: int + +### Permission Delete +#### Parameters: +Query takes a `MultiFieldReq` + +#### Returns: +class: `PermissionDeleteResponse`. A basic API response with response code as 202 if successful. + +### Permission Find +#### Parameters: +Query takes a `MultiFieldReq` + +#### Returns: +class: `PermissionFindResponse` with list of Permissions. + +### Permission Current +#### Parameters: +Query takes a `MultiFieldReq` + +#### Returns: +class: `PermissionCurrentResponse` with set of current permissions + +```go +resp := <-fcClient.PermissionCreate(permissionRequest) +fmt.Printf("Permission Create API Response: %v", resp) +if resp.IsSuccessful { + fmt.Println("Permission Created Successfully!") +} +resp = <-fcClient.PermissionVerify(permissionRequest) +fmt.Printf("Permission Verify API Response: %v", resp) +if resp.IsSuccessful { + fmt.Printf("Permissions List: %v", resp.PermissionVerifyResponse) +} + +resp = <-fcClient.PermissionDelete(multifieldRequest) +fmt.Printf("Permission Delete API Response: %v", resp) +if resp.IsSuccessful { + fmt.Println("Permission Deleted Successfully!") +} + +resp = <-fcClient.PermissionFind(multifieldRequest) +fmt.Printf("Permission Find API Response: %v", resp) +if resp.IsSuccessful { + fmt.Printf("Permission Find: %v", resp.PermissionFindResponse) +} + +resp = <-fcClient.PermissionCurrent(multifieldRequest) +fmt.Printf("Permission Current API Response: %v", resp) +if resp.IsSuccessful { + fmt.Printf("Permission Current: %v", resp.PermissionCurrentResponse) +} +``` diff --git a/example/main.go b/example/main.go index e92050b..69097f2 100644 --- a/example/main.go +++ b/example/main.go @@ -191,4 +191,71 @@ func main() { if resp.IsSuccessful { fmt.Println(resp.EmailVerificationResponse) } + + //Permission + //Permission Create + + multifieldRequest, err := fc.NewMultifieldRequest( + fc.WithEmailForMultifieldRequest("bart@fullcontact.com")) + + consentPurpose := fc.NewConsentPurpose( + fc.WithConsentPurposeId(1), + fc.WithConsentPurposeChannel("web"), + fc.WithConsentPurposeTtl(365), + fc.WithConsentPurposeEnabled(true)) + + permissionCreateRequest, err := fc.NewPermissionRequest( + fc.WithMultifieldRequestForPermission(multifieldRequest), + fc.WithConsentPurposeForPermission(consentPurpose), + fc.WithCollectionMethodForPermission("cookiePopUp"), + fc.WithCollectionLocationForPermission("https://kenblahblah.com"), + fc.WithPolicyUrlForPermission("http://foo.baz"), + fc.WithTermsServiceForPermission("http://foo.tos")) + if err != nil { + log.Fatalln(err) + return + } + + //Sending Permission Create request + resp = <- fcClient.PermissionCreate(permissionCreateRequest) + fmt.Printf("Permission Create API Response: %v", resp) + if resp.IsSuccessful == true { + fmt.Printf("Permission Create Response: %v", resp) + } + + //Permission Find + //Sending Permission Find request which returns a channel of type `APIResponse` + resp = <- fcClient.PermissionFind(multifieldRequest) + fmt.Printf("Permission Find API Response: %v", resp) + if resp.IsSuccessful == true { + fmt.Printf("Permission Find Response: %v", resp.PermissionFindResponse) + } + + //Permission Current + //Sending Permission Current request which returns a channel of type `APIResponse` + resp = <- fcClient.PermissionCurrent(multifieldRequest) + fmt.Printf("Permission Current API Response: %v", resp) + if resp.IsSuccessful == true { + fmt.Printf("Permission Current Response: %v", resp.PermissionCurrentResponse) + } + + //Permission Verify + permissionVerifyRequest, err := fc.NewPermissionRequest( + fc.WithMultifieldRequestForPermission(multifieldRequest), + fc.WithPurposeIdForPermission(1), + fc.WithChannelForPermission("web")) + + //Sending Permission Verify request which returns a channel of type `APIResponse` + resp = <- fcClient.PermissionVerify(permissionVerifyRequest) + fmt.Printf("Permission Verify API Response: %v", resp) + if resp.IsSuccessful == true { + fmt.Printf("Permission Verify Response: %v", resp.PermissionVerifyResponse) + } + + //Permission Delete + //Sending Permission Delete request + resp = <- fcClient.PermissionDelete(multifieldRequest) + if resp.IsSuccessful == true { + fmt.Printf("Permission Delete API Response: %v", resp) + } } diff --git a/fc/api_response.go b/fc/api_response.go index c9ceeab..74fefa7 100644 --- a/fc/api_response.go +++ b/fc/api_response.go @@ -6,26 +6,30 @@ import ( ) type APIResponse struct { - RawHttpResponse *http.Response - PersonResponse *PersonResp - CompanyResponse *CompanyResponse - CompanySearchResponse []*CompanySearchResponse - ResolveResponse *ResolveResponse - ResolveResponseWithTags *ResolveResponseWithTags - EmailVerificationResponse *EmailVerificationResponse - TagsResponse *TagsResponse - AudienceResponse *AudienceResponse - StatusCode int - Status string - IsSuccessful bool - Err error + RawHttpResponse *http.Response + PersonResponse *PersonResp + CompanyResponse *CompanyResponse + CompanySearchResponse []*CompanySearchResponse + ResolveResponse *ResolveResponse + ResolveResponseWithTags *ResolveResponseWithTags + EmailVerificationResponse *EmailVerificationResponse + TagsResponse *TagsResponse + AudienceResponse *AudienceResponse + PermissionFindResponse []*PermissionFindResponse + PermissionCurrentResponse map[string]map[string]ConsentPurposeResponse + PermissionVerifyResponse *ConsentPurposeResponse + StatusCode int + Status string + IsSuccessful bool + Err error } func (resp *APIResponse) String() string { return fmt.Sprintf("\nRawHttpResponse: %v,\nPersonResponse: %v,\nCompanyResponse: %v,\nCompanySearchResponse: %v,"+ "\nResolveResponse: %v,\nResolveResponseWithTags: %v,\nTagsResponse: %v,\nAudienceResponse: %v,\nEmailVerificationResponse: %v,"+ - "\nStatusCode: %v,\nStatus: %v,\nIsSuccessful: %v,\nErr: %v\n", + "\nPermissionFindResponse: %v,\nPermissionCurrentResponse: %v,\nPermissionVerifyResponse: %v,\nStatusCode: %v,\nStatus: %v,\nIsSuccessful: %v,\nErr: %v\n", resp.RawHttpResponse, resp.PersonResponse, resp.CompanyResponse, resp.CompanySearchResponse, resp.ResolveResponse, resp.ResolveResponseWithTags, resp.TagsResponse, resp.AudienceResponse, resp.EmailVerificationResponse, + resp.PermissionFindResponse, resp.PermissionCurrentResponse, resp.PermissionVerifyResponse, resp.StatusCode, resp.Status, resp.IsSuccessful, resp.Err) } diff --git a/fc/constants.go b/fc/constants.go index 2abd94c..4242e58 100644 --- a/fc/constants.go +++ b/fc/constants.go @@ -1,7 +1,7 @@ package fullcontact const ( - version = "1.1.1" + version = "1.2.0" userAgent = "FullContact_Go_Client_V" + version FcApiKey = "FC_API_KEY" FCGoClientTestType = "FCGoClientTestType" @@ -20,4 +20,9 @@ const ( audienceCreateUrl = baseUrl + "audience.create" audienceDownloadUrl = baseUrl + "audience.download" emailVerificationUrl = v2BaseUrl + "verification/email" + permissionCreateUrl = baseUrl + "permission.create" + permissionDeleteUrl = baseUrl + "permission.delete" + permissionFindUrl = baseUrl + "permission.find" + permissionCurrentUrl = baseUrl + "permission.current" + permissionVerifyUrl = baseUrl + "permission.verify" ) diff --git a/fc/fullcontact.go b/fc/fullcontact.go index 7a63868..d78274a 100644 --- a/fc/fullcontact.go +++ b/fc/fullcontact.go @@ -132,8 +132,16 @@ func sendToChannel(ch chan *APIResponse, response *http.Response, url string, er setTagsResponse(apiResponse) case audienceCreateUrl, audienceDownloadUrl: setAudienceResponse(apiResponse) - case emailVerificationUrl: - setEmailVerificationResponse(apiResponse) + case permissionCreateUrl: + setPermissionCreateResponse(apiResponse) + case permissionDeleteUrl: + setPermissionDeleteResponse(apiResponse) + case permissionFindUrl: + setPermissionFindResponse(apiResponse) + case permissionCurrentUrl: + setPermissionCurrentResponse(apiResponse) + case permissionVerifyUrl: + setPermissionVerifyResponse(apiResponse) } } ch <- apiResponse @@ -394,6 +402,127 @@ func (fcClient *fullContactClient) EmailVerification(email string) chan *APIResp return ch } +/* Permission +FullContact Permission API - PermissionCreate, takes an PermissionRequest and returns a channel of type APIResponse. +Request is converted to JSON and sends a Asynchronous request */ +func (fcClient *fullContactClient) PermissionCreate(permissionRequest *PermissionRequest) chan *APIResponse { + ch := make(chan *APIResponse) + if permissionRequest == nil { + go sendToChannel(ch, nil, "", NewFullContactError("Permission Request can't be nil")) + return ch + } + err := validateForPermissionCreate(permissionRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + + reqBytes, err := json.Marshal(permissionRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + // Send Asynchronous Request in Goroutine + go fcClient.do(permissionCreateUrl, reqBytes, ch) + return ch +} + +/* FullContact Permission API - PermissionDelete, takes an PermissionRequest and returns a channel of type APIResponse. +Request is converted to JSON and sends a Asynchronous request */ +func (fcClient *fullContactClient) PermissionDelete(multifieldRequest *MultifieldRequest) chan *APIResponse { + ch := make(chan *APIResponse) + if multifieldRequest == nil { + go sendToChannel(ch, nil, "", NewFullContactError("Multifield Request can't be nil")) + return ch + } + err := validateForPermissionDelete(multifieldRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + + reqBytes, err := json.Marshal(multifieldRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + // Send Asynchronous Request in Goroutine + go fcClient.do(permissionDeleteUrl, reqBytes, ch) + return ch +} + +/* FullContact Permission API - PermissionFind, takes an PermissionRequest and returns a channel of type APIResponse. +Request is converted to JSON and sends a Asynchronous request */ +func (fcClient *fullContactClient) PermissionFind(multifieldRequest *MultifieldRequest) chan *APIResponse { + ch := make(chan *APIResponse) + if multifieldRequest == nil { + go sendToChannel(ch, nil, "", NewFullContactError("Multifield Request can't be nil")) + return ch + } + err := validateForPermissionFind(multifieldRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + + reqBytes, err := json.Marshal(multifieldRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + // Send Asynchronous Request in Goroutine + go fcClient.do(permissionFindUrl, reqBytes, ch) + return ch +} + +/* FullContact Permission API - PermissionCurrent, takes an PermissionRequest and returns a channel of type APIResponse. +Request is converted to JSON and sends a Asynchronous request */ +func (fcClient *fullContactClient) PermissionCurrent(multifieldRequest *MultifieldRequest) chan *APIResponse { + ch := make(chan *APIResponse) + if multifieldRequest == nil { + go sendToChannel(ch, nil, "", NewFullContactError("Multifield Request can't be nil")) + return ch + } + err := validateForPermissionCurrent(multifieldRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + + reqBytes, err := json.Marshal(multifieldRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + // Send Asynchronous Request in Goroutine + go fcClient.do(permissionCurrentUrl, reqBytes, ch) + return ch +} + +/* FullContact Permission API - PermissionVerify, takes an PermissionRequest and returns a channel of type APIResponse. +Request is converted to JSON and sends a Asynchronous request */ +func (fcClient *fullContactClient) PermissionVerify(permissionRequest *PermissionRequest) chan *APIResponse { + ch := make(chan *APIResponse) + if permissionRequest == nil { + go sendToChannel(ch, nil, "", NewFullContactError("Permission Request can't be nil")) + return ch + } + err := validateForPermissionVerify(permissionRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + + reqBytes, err := json.Marshal(permissionRequest) + if err != nil { + go sendToChannel(ch, nil, "", err) + return ch + } + // Send Asynchronous Request in Goroutine + go fcClient.do(permissionVerifyUrl, reqBytes, ch) + return ch +} + func setPersonResponse(apiResponse *APIResponse) { bodyBytes, err := ioutil.ReadAll(apiResponse.RawHttpResponse.Body) defer apiResponse.RawHttpResponse.Body.Close() @@ -567,6 +696,93 @@ func setEmailVerificationResponse(apiResponse *APIResponse) { apiResponse.EmailVerificationResponse = &emailResponse } +func setPermissionCreateResponse(apiResponse *APIResponse) { + _, err := ioutil.ReadAll(apiResponse.RawHttpResponse.Body) + defer apiResponse.RawHttpResponse.Body.Close() + if err != nil { + apiResponse.Err = err + return + } + apiResponse.Status = apiResponse.RawHttpResponse.Status + apiResponse.StatusCode = apiResponse.RawHttpResponse.StatusCode + apiResponse.IsSuccessful = (apiResponse.StatusCode == 200) || (apiResponse.StatusCode == 202) || (apiResponse.StatusCode == 404) +} + +func setPermissionDeleteResponse(apiResponse *APIResponse) { + _, err := ioutil.ReadAll(apiResponse.RawHttpResponse.Body) + defer apiResponse.RawHttpResponse.Body.Close() + if err != nil { + apiResponse.Err = err + return + } + apiResponse.Status = apiResponse.RawHttpResponse.Status + apiResponse.StatusCode = apiResponse.RawHttpResponse.StatusCode + apiResponse.IsSuccessful = (apiResponse.StatusCode == 200) || (apiResponse.StatusCode == 202) || (apiResponse.StatusCode == 404) +} + +func setPermissionFindResponse(apiResponse *APIResponse) { + bodyBytes, err := ioutil.ReadAll(apiResponse.RawHttpResponse.Body) + defer apiResponse.RawHttpResponse.Body.Close() + if err != nil { + apiResponse.Err = err + return + } + var response []*PermissionFindResponse + if isPopulated(string(bodyBytes)) { + err = json.Unmarshal(bodyBytes, &response) + if err != nil { + apiResponse.Err = err + return + } + } + apiResponse.Status = apiResponse.RawHttpResponse.Status + apiResponse.StatusCode = apiResponse.RawHttpResponse.StatusCode + apiResponse.IsSuccessful = (apiResponse.StatusCode == 200) || (apiResponse.StatusCode == 202) || (apiResponse.StatusCode == 404) + apiResponse.PermissionFindResponse = response +} + +func setPermissionVerifyResponse(apiResponse *APIResponse) { + bodyBytes, err := ioutil.ReadAll(apiResponse.RawHttpResponse.Body) + defer apiResponse.RawHttpResponse.Body.Close() + if err != nil { + apiResponse.Err = err + return + } + var response ConsentPurposeResponse + if isPopulated(string(bodyBytes)) { + err = json.Unmarshal(bodyBytes, &response) + if err != nil { + apiResponse.Err = err + return + } + } + apiResponse.Status = apiResponse.RawHttpResponse.Status + apiResponse.StatusCode = apiResponse.RawHttpResponse.StatusCode + apiResponse.IsSuccessful = (apiResponse.StatusCode == 200) || (apiResponse.StatusCode == 202) || (apiResponse.StatusCode == 404) + apiResponse.PermissionVerifyResponse = &response +} + +func setPermissionCurrentResponse(apiResponse *APIResponse) { + bodyBytes, err := ioutil.ReadAll(apiResponse.RawHttpResponse.Body) + defer apiResponse.RawHttpResponse.Body.Close() + if err != nil { + apiResponse.Err = err + return + } + var response map[string]map[string]ConsentPurposeResponse + if isPopulated(string(bodyBytes)) { + err = json.Unmarshal(bodyBytes, &response) + if err != nil { + apiResponse.Err = err + return + } + } + apiResponse.Status = apiResponse.RawHttpResponse.Status + apiResponse.StatusCode = apiResponse.RawHttpResponse.StatusCode + apiResponse.IsSuccessful = (apiResponse.StatusCode == 200) || (apiResponse.StatusCode == 202) || (apiResponse.StatusCode == 404) + apiResponse.PermissionCurrentResponse = response +} + func min(x, y int) int { if x < y { return x diff --git a/fc/multifield_request.go b/fc/multifield_request.go new file mode 100644 index 0000000..cd82777 --- /dev/null +++ b/fc/multifield_request.go @@ -0,0 +1,172 @@ +package fullcontact + +type MultifieldRequestOption func(ar *MultifieldRequest) + +type MultifieldRequest struct { + Emails []string `json:"emails,omitempty"` + Phones []string `json:"phones,omitempty"` + Maids []string `json:"maids,omitempty"` + Location *Location `json:"location,omitempty"` + Name *PersonName `json:"name,omitempty"` + Profiles []*Profile `json:"profiles,omitempty"` + PersonId string `json:"personId,omitempty"` + RecordId string `json:"recordId,omitempty"` + PartnerId string `json:"partnerId,omitempty"` + LiNonId string `json:"li_nonid,omitempty"` + Placekey string `json:"placekey,omitempty"` +} + +func NewMultifieldRequest(option ...MultifieldRequestOption) (*MultifieldRequest, error) { + multifieldRequest := &MultifieldRequest{} + + for _, opt := range option { + opt(multifieldRequest) + } + return multifieldRequest, nil +} + +func (multifieldRequest *MultifieldRequest) isQueryable() bool { + return multifieldRequest.Emails != nil || + multifieldRequest.Phones != nil || + multifieldRequest.Profiles != nil || + multifieldRequest.Maids != nil || + isPopulated(multifieldRequest.LiNonId) +} + +func (multifieldRequest *MultifieldRequest) isValidName() bool { + return (isPopulated(multifieldRequest.Name.Full)) || + (isPopulated(multifieldRequest.Name.Given) && isPopulated(multifieldRequest.Name.Family)) +} + +func (multifieldRequest *MultifieldRequest) isValidLocation() bool { + return isPopulated(multifieldRequest.Location.AddressLine1) && + ((isPopulated(multifieldRequest.Location.City) && + (isPopulated(multifieldRequest.Location.Region) || isPopulated(multifieldRequest.Location.RegionCode))) || + (isPopulated(multifieldRequest.Location.PostalCode))) +} + +func (multifieldRequest *MultifieldRequest) validate() error { + if !multifieldRequest.isQueryable() { + if multifieldRequest.Location == nil && multifieldRequest.Name == nil && !isPopulated(multifieldRequest.Placekey) { + return nil + } else if (multifieldRequest.Location != nil || isPopulated(multifieldRequest.Placekey)) && multifieldRequest.Name != nil { + if (multifieldRequest.Location != nil && multifieldRequest.isValidLocation()) || isPopulated(multifieldRequest.Placekey) { + if multifieldRequest.isValidName() { + return nil + } else { + return NewFullContactError("Name data requires full name or given and family name") + } + } else { + return NewFullContactError("A valid placekey is required or Location data requires addressLine1 and postalCode or addressLine1, city and regionCode (or region)") + } + } + return NewFullContactError( + "If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") + } + return nil +} + +func WithMaidsForMultifieldRequest(maid string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Maids == nil { + multifieldRequest.Maids = make([]string, 0) + } + multifieldRequest.Maids = append(multifieldRequest.Maids, maid) + } +} + +func WithEmailForMultifieldRequest(email string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Emails == nil { + multifieldRequest.Emails = make([]string, 0) + } + multifieldRequest.Emails = append(multifieldRequest.Emails, email) + } +} + +func WithEmailsForMultifieldRequest(emails []string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Emails == nil { + multifieldRequest.Emails = make([]string, 0) + } + multifieldRequest.Emails = append(multifieldRequest.Emails, emails...) + } +} + +func WithPhoneForMultifieldRequest(phone string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Phones == nil { + multifieldRequest.Phones = make([]string, 0) + } + multifieldRequest.Phones = append(multifieldRequest.Phones, phone) + } +} + +func WithPhonesForMultifieldRequest(phones []string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Phones == nil { + multifieldRequest.Phones = make([]string, 0) + } + multifieldRequest.Phones = append(multifieldRequest.Phones, phones...) + } +} + +func WithProfileForMultifieldRequest(profile *Profile) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Profiles == nil { + multifieldRequest.Profiles = make([]*Profile, 0) + } + multifieldRequest.Profiles = append(multifieldRequest.Profiles, profile) + } +} + +func WithProfilesForMultifieldRequest(profiles []*Profile) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + if multifieldRequest.Profiles == nil { + multifieldRequest.Profiles = make([]*Profile, 0) + } + multifieldRequest.Profiles = append(multifieldRequest.Profiles, profiles...) + } +} + +func WithNameForMultifieldRequest(name *PersonName) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.Name = name + } +} + +func WithLocationForMultifieldRequest(location *Location) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.Location = location + } +} + +func WithLiNonIdForMultifieldRequest(liNonId string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.LiNonId = liNonId + } +} + +func WithPersonIdForMultifieldRequest(personId string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.PersonId = personId + } +} + +func WithRecordIdForMultifieldRequest(recordId string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.RecordId = recordId + } +} + +func WithPartnerIdForMultifieldRequest(partnerId string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.PartnerId = partnerId + } +} + +func WithPlacekeyForMultifieldRequest(placekey string) MultifieldRequestOption { + return func(multifieldRequest *MultifieldRequest) { + multifieldRequest.Placekey = placekey + } +} diff --git a/fc/permission.go b/fc/permission.go new file mode 100644 index 0000000..11deafd --- /dev/null +++ b/fc/permission.go @@ -0,0 +1,25 @@ +package fullcontact + +type PermissionFindResponse struct { + PermissionType string `json:"permissionType"` + PermissionId string `json:"permissionId"` + ConsentPurposes []*ConsentPurposeResponse `json:"consentPurposes"` + Locale string `json:"locale"` + IpAddress string `json:"ipAddress"` + Language string `json:"language"` + CollectionMethod string `json:"collectionMethod"` + CollectionLocation string `json:"collectionLocation"` + PolicyUrl string `json:"policyUrl"` + TermsService string `json:"termsService"` + Timestamp int `json:"timestamp"` + Created int `json:"created"` +} + +type ConsentPurposeResponse struct { + Ttl int `json:"ttl"` + Enabled bool `json:"enabled"` + Channel string `json:"channel"` + PurposeId int `json:"purposeId"` + PurposeName string `json:"purposeName"` + Timestamp int `json:"timestamp"` +} diff --git a/fc/permission_request.go b/fc/permission_request.go new file mode 100644 index 0000000..faf5de8 --- /dev/null +++ b/fc/permission_request.go @@ -0,0 +1,242 @@ +package fullcontact + +//Permission Request + +type PermissionRequestOption func(ar *PermissionRequest) + +type PermissionRequest struct { + Timestamp int `json:"timestamp,omitempty"` + Query *MultifieldRequest `json:"query,omitempty"` + ConsentPurposes []*ConsentPurpose `json:"consentPurposes,omitempty"` + Locale string `json:"locale,omitempty"` + IpAddress string `json:"ipAddress,omitempty"` + Language string `json:"language,omitempty"` + CollectionMethod string `json:"collectionMethod,omitempty"` + CollectionLocation string `json:"collectionLocation,omitempty"` + Tcf string `json:"tcf,omitempty"` + PolicyUrl string `json:"policyUrl,omitempty"` + TermsService string `json:"termsService,omitempty"` + PurposeId int `json:"purposeId,omitempty"` + Channel string `json:"channel,omitempty"` +} + +func NewPermissionRequest(option ...PermissionRequestOption) (*PermissionRequest, error) { + permissionRequest := &PermissionRequest{} + + for _, opt := range option { + opt(permissionRequest) + } + return permissionRequest, nil +} + +func validateForPermissionCreateFields(request *PermissionRequest) error { + if request.ConsentPurposes == nil { + return NewFullContactError("At least 1 `consentPurpose` is Required for PermissionRequest") + } else if !isPopulated(request.CollectionMethod) { + return NewFullContactError("Collection Method is required for PermissionRequest") + } else if !isPopulated(request.CollectionLocation) { + return NewFullContactError("Collection Location is required for PermissionRequest") + } else if !isPopulated(request.PolicyUrl) { + return NewFullContactError("Policy URL is required for PermissionRequest") + } else if !isPopulated(request.TermsService) { + return NewFullContactError("Terms of Service is required for PermissionRequest") + } + return nil +} + +func validateForPermissionVerifyFields(request *PermissionRequest) error { + if request.PurposeId == 0 { + return NewFullContactError("Purpose ID is required for PermissionRequest") + } else if !isPopulated(request.Channel) { + return NewFullContactError("Channel is required for PermissionRequest") + } + return nil +} + +func validateForPermissionCreate(request *PermissionRequest) error { + err := validateForPermissionCreateFields(request) + if err != nil { + return err + } + for _, consentPurpose := range request.ConsentPurposes{ + err = validateConsentPurpose(consentPurpose) + if err != nil { + return err + } + } + err = request.Query.validate() + return err +} + +func validateForPermissionDelete(request *MultifieldRequest) error { + err := request.validate() + return err +} + +func validateForPermissionFind(request *MultifieldRequest) error { + err := request.validate() + return err +} + +func validateForPermissionCurrent(request *MultifieldRequest) error { + err := request.validate() + return err +} + +func validateForPermissionVerify(request *PermissionRequest) error { + err := validateForPermissionVerifyFields(request) + if err != nil { + return err + } + err = request.Query.validate() + return err +} + +func WithMultifieldRequestForPermission(multifieldRequest *MultifieldRequest) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.Query = multifieldRequest + } +} + +func WithConsentPurposeForPermission(consentPurpose *ConsentPurpose) PermissionRequestOption { + return func(pr *PermissionRequest) { + if pr.ConsentPurposes == nil { + pr.ConsentPurposes = make([]*ConsentPurpose, 0) + } + pr.ConsentPurposes = append(pr.ConsentPurposes, consentPurpose) + } +} + +func WithConsentPurposesForPermission(consentPurpose []*ConsentPurpose) PermissionRequestOption { + return func(pr *PermissionRequest) { + if pr.ConsentPurposes == nil { + pr.ConsentPurposes = make([]*ConsentPurpose, 0) + } + pr.ConsentPurposes = append(pr.ConsentPurposes, consentPurpose...) + } +} + +func WithLocaleForPermission(locale string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.Locale = locale + } +} + +func WithIpAddressForPermission(ipAddress string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.IpAddress = ipAddress + } +} + +func WithLanguageForPermission(language string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.Language = language + } +} + +func WithCollectionMethodForPermission(collectionMethod string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.CollectionMethod = collectionMethod + } +} + +func WithCollectionLocationForPermission(collectionLocation string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.CollectionLocation = collectionLocation + } +} + +func WithTcfForPermission(tcf string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.Tcf = tcf + } +} + +func WithPolicyUrlForPermission(policyUrl string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.PolicyUrl = policyUrl + } +} + +func WithTermsServiceForPermission(termsService string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.TermsService = termsService + } +} + +func WithPurposeIdForPermission(purposeId int) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.PurposeId = purposeId + } +} + +func WithChannelForPermission(channel string) PermissionRequestOption { + return func(pr *PermissionRequest) { + pr.Channel = channel + } +} + +type ConsentPurposeOption func(consentPurposes *ConsentPurpose) + +type ConsentPurpose struct { + PurposeId int `json:"purposeId"` + Channel []string `json:"channel"` + Ttl int `json:"ttl"` + Enabled *bool `json:"enabled"` +} + +func NewConsentPurpose(options ...ConsentPurposeOption) *ConsentPurpose { + consentPurpose := &ConsentPurpose{} + for _, opts := range options { + opts(consentPurpose) + } + return consentPurpose +} + +func validateConsentPurpose(consentPurpose *ConsentPurpose) error { + if consentPurpose.PurposeId == 0 { + return NewFullContactError("Purpose id is required for consentPurpose") + } else if consentPurpose.Channel == nil { + return NewFullContactError("Channel is required for consentPurpose") + } else if consentPurpose.Enabled == nil { + return NewFullContactError("Enabled is required for consentPurpose") + } + return nil +} + +func WithConsentPurposeId(purposeId int) ConsentPurposeOption { + return func(consentPurpose *ConsentPurpose) { + consentPurpose.PurposeId = purposeId + } +} + +func WithConsentPurposeChannel(channel string) ConsentPurposeOption { + return func(consentPurpose *ConsentPurpose) { + if consentPurpose.Channel == nil { + consentPurpose.Channel = make([]string, 0) + } + consentPurpose.Channel = append(consentPurpose.Channel, channel) + } +} + +func WithConsentPurposeChannels(channel []string) ConsentPurposeOption { + return func(consentPurpose *ConsentPurpose) { + if consentPurpose.Channel == nil { + consentPurpose.Channel = make([]string, 0) + } + consentPurpose.Channel = append(consentPurpose.Channel, channel...) + } +} + +func WithConsentPurposeTtl(ttl int) ConsentPurposeOption { + return func(consentPurpose *ConsentPurpose) { + consentPurpose.Ttl = ttl + } +} + +func WithConsentPurposeEnabled(enabled bool) ConsentPurposeOption { + return func(consentPurpose *ConsentPurpose) { + consentPurpose.Enabled = &enabled + } +} + diff --git a/fc/permission_request_test.go b/fc/permission_request_test.go new file mode 100644 index 0000000..49dcd5b --- /dev/null +++ b/fc/permission_request_test.go @@ -0,0 +1,640 @@ +package fullcontact + +import ( + "encoding/json" + assert "github.com/stretchr/testify/require" + "testing" +) + +func TestNewPermissionRequestForCreate(t *testing.T) { + emails := []string{"test1@gmail.com", "test2@outlook.com"} + profile1, err := NewProfile(WithUrl("https://twitter.com/mcreedy")) + assert.NoError(t, err) + profile2, err := NewProfile(WithUrl("https://twitter.com/mcreedytest")) + assert.NoError(t, err) + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, err := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Marian C Reed"), WithFamily("Reed"), WithGiven("Marian"))), + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithEmailsForMultifieldRequest(emails), + WithPhoneForMultifieldRequest("123-4567890"), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124"))), + WithProfileForMultifieldRequest(profile1), + WithProfileForMultifieldRequest(profile2), + WithMaidsForMultifieldRequest("abcd-123-abcd-1234-abcdlkjhasdfgh"), + WithMaidsForMultifieldRequest("1234-snbk-lkldiemvmruixp-2kdp-vdm"),) + assert.NoError(t, err) + requestJson := "{\"query\":{\"emails\":[\"marianrd97@outlook.com\",\"test1@gmail.com\",\"test2@outlook.com\"],\"phones\":[\"123-4567890\"],\"maids\":[\"abcd-123-abcd-1234-abcdlkjhasdfgh\",\"1234-snbk-lkldiemvmruixp-2kdp-vdm\"],\"location\":{\"addressLine1\":\"123/23\",\"addressLine2\":\"Some Street\",\"city\":\"Denver\",\"region\":\"Denver\",\"regionCode\":\"123123\",\"postalCode\":\"23124\"},\"name\":{\"given\":\"Marian\",\"family\":\"Reed\",\"full\":\"Marian C Reed\"},\"profiles\":[{\"url\":\"https://twitter.com/mcreedy\"},{\"url\":\"https://twitter.com/mcreedytest\"}]},\"consentPurposes\":[{\"purposeId\":1,\"channel\":[\"web\"],\"ttl\":365,\"enabled\":true}],\"locale\":\"US\",\"ipAddress\":\"127.0.0.1\",\"language\":\"en\",\"collectionMethod\":\"cookiePopUp\",\"collectionLocation\":\"https://kenblahblah.com\",\"tcf\":\"some.valid.tcfv2.string\",\"policyUrl\":\"http://foo.baz\",\"termsService\":\"http://foo.tos\"}" + pr, err := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithLocaleForPermission("US"), + WithIpAddressForPermission("127.0.0.1"), + WithLanguageForPermission("en"), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithTcfForPermission("some.valid.tcfv2.string"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + assert.NoError(t, err) + reqBytes, err := json.Marshal(pr) + assert.NoError(t, err) + assert.Equal(t, requestJson, string(reqBytes)) +} + +func TestNewPermissionRequestForFind(t *testing.T) { + emails := []string{"test1@gmail.com", "test2@outlook.com"} + profile1, err := NewProfile(WithUrl("https://twitter.com/mcreedy")) + assert.NoError(t, err) + profile2, err := NewProfile(WithUrl("https://twitter.com/mcreedytest")) + assert.NoError(t, err) + requestJson := "{\"emails\":[\"marianrd97@outlook.com\",\"test1@gmail.com\",\"test2@outlook.com\"],\"phones\":[\"123-4567890\"],\"maids\":[\"abcd-123-abcd-1234-abcdlkjhasdfgh\",\"1234-snbk-lkldiemvmruixp-2kdp-vdm\"],\"location\":{\"addressLine1\":\"123/23\",\"addressLine2\":\"Some Street\",\"city\":\"Denver\",\"region\":\"Denver\",\"regionCode\":\"123123\",\"postalCode\":\"23124\"},\"name\":{\"given\":\"Marian\",\"family\":\"Reed\",\"full\":\"Marian C Reed\"},\"profiles\":[{\"url\":\"https://twitter.com/mcreedy\"},{\"url\":\"https://twitter.com/mcreedytest\"}]}" + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Marian C Reed"), WithFamily("Reed"), WithGiven("Marian"))), + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithEmailsForMultifieldRequest(emails), + WithPhoneForMultifieldRequest("123-4567890"), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124"))), + WithProfileForMultifieldRequest(profile1), + WithProfileForMultifieldRequest(profile2), + WithMaidsForMultifieldRequest("abcd-123-abcd-1234-abcdlkjhasdfgh"), + WithMaidsForMultifieldRequest("1234-snbk-lkldiemvmruixp-2kdp-vdm")) + pr, err := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest)) + assert.NoError(t, err) + reqBytes, err := json.Marshal(pr.Query) + assert.NoError(t, err) + assert.Equal(t, requestJson, string(reqBytes)) +} + +func TestNewPermissionRequestForVerify(t *testing.T) { + emails := []string{"test1@gmail.com", "test2@outlook.com"} + profile1, err := NewProfile(WithUrl("https://twitter.com/mcreedy")) + assert.NoError(t, err) + profile2, err := NewProfile(WithUrl("https://twitter.com/mcreedytest")) + assert.NoError(t, err) + requestJson := "{\"query\":{\"emails\":[\"marianrd97@outlook.com\",\"test1@gmail.com\",\"test2@outlook.com\"],\"phones\":[\"123-4567890\"],\"maids\":[\"abcd-123-abcd-1234-abcdlkjhasdfgh\",\"1234-snbk-lkldiemvmruixp-2kdp-vdm\"],\"location\":{\"addressLine1\":\"123/23\",\"addressLine2\":\"Some Street\",\"city\":\"Denver\",\"region\":\"Denver\",\"regionCode\":\"123123\",\"postalCode\":\"23124\"},\"name\":{\"given\":\"Marian\",\"family\":\"Reed\",\"full\":\"Marian C Reed\"},\"profiles\":[{\"url\":\"https://twitter.com/mcreedy\"},{\"url\":\"https://twitter.com/mcreedytest\"}]},\"purposeId\":8,\"channel\":\"web\"}" + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Marian C Reed"), WithFamily("Reed"), WithGiven("Marian"))), + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithEmailsForMultifieldRequest(emails), + WithPhoneForMultifieldRequest("123-4567890"), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124"))), + WithProfileForMultifieldRequest(profile1), + WithProfileForMultifieldRequest(profile2), + WithMaidsForMultifieldRequest("abcd-123-abcd-1234-abcdlkjhasdfgh"), + WithMaidsForMultifieldRequest("1234-snbk-lkldiemvmruixp-2kdp-vdm")) + pr, err := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithPurposeIdForPermission(8), + WithChannelForPermission("web")) + assert.NoError(t, err) + reqBytes, err := json.Marshal(pr) + assert.NoError(t, err) + assert.Equal(t, requestJson, string(reqBytes)) +} + +func TestNewPermissionRequestWithoutNameAndLocation(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com")) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithNameOnlyWithQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithNameForMultifieldRequest(&PersonName{ + Full: "Marian C Reed", + })) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithNameOnlyWithoutQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{ + Full: "Marian C Reed", + })) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") +} + +func TestNewPermissionRequestWithLocationOnlyWithQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithLocationOnlyWithoutQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124")))) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") +} + +func TestNewPermissionRequestWithLocationWithoutAddressLine1WithQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithLocationWithoutAddressLine1WithoutQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("Denver"), + WithRegionCode("123123"), + WithPostalCode("23124")))) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: A valid placekey is required or Location data requires addressLine1 and postalCode or addressLine1, city and regionCode (or region)") +} + +func TestNewPermissionRequestWithLocationOnlyAddressLine1WithQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithLocationOnlyAddressLine1WithoutQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23")))) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: A valid placekey is required or Location data requires addressLine1 and postalCode or addressLine1, city and regionCode (or region)") +} + +func TestNewPermissionRequestWithLocationWithAddressLine1AndCityWithQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithCity("Denver")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithLocationWithAddressLine1AndCityWithoutQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithCity("Denver")))) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: A valid placekey is required or Location data requires addressLine1 and postalCode or addressLine1, city and regionCode (or region)") +} + +func TestNewPermissionRequestWithLocationWithAddressLine1AndRegionWithQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithEmailForMultifieldRequest("marianrd97@outlook.com"), + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithRegionCode("123123")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithLocationWithAddressLine1AndRegionWithoutQueryable(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(NewPersonName(WithFull("Test Name"))), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithRegionCode("123123")))) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: A valid placekey is required or Location data requires addressLine1 and postalCode or addressLine1, city and regionCode (or region)") +} + +func TestNewPermissionRequestWithValidLocation1(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Full: "Marian C Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("12343")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithValidLocation2(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Full: "Marian C Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithCity("Denver"), + WithRegionCode("123123")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithValidLocation3(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Full: "Marian C Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithAddressLine2("Some Street"), + WithCity("Denver"), + WithRegionForLocation("123123")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithValidName(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithValidNameWithValidPlacekey(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithPlacekeyForMultifieldRequest("226@5z4-zvy-ffz")) + err := validateForPermissionFind(multifieldRequest) + assert.NoError(t, err) +} + +func TestNewPermissionRequestWithPlacekeyOnly(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithPlacekeyForMultifieldRequest("226@5z4-zvy-ffz")) + err := validateForPermissionFind(multifieldRequest) + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") +} + +func TestNewPermissionRequestForCreateWithConsentPurpose(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.NoError(t, err) +} + +func TestNewPermissionRequestForCreateWithMultipleConsentPurpose(t *testing.T) { + consentPurpose1 := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + consentPurpose2 := NewConsentPurpose( + WithConsentPurposeId(2), + WithConsentPurposeChannel("phone"), + WithConsentPurposeChannel("mobile"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurpose1), + WithConsentPurposeForPermission(consentPurpose2), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.NoError(t, err) +} + +func TestNewPermissionRequestForCreateWithMultipleConsentPurposeWithoutChannel(t *testing.T) { + consentPurpose1 := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + consentPurpose2 := NewConsentPurpose( + WithConsentPurposeId(2), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurpose1), + WithConsentPurposeForPermission(consentPurpose2), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Channel is required for consentPurpose") +} + +func TestNewPermissionRequestForCreateWithoutConsentPurposeId(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(NewConsentPurpose( + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true))), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Purpose id is required for consentPurpose") +} + +func TestNewPermissionRequestForCreateWithoutConsentPurposeChannel(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Channel is required for consentPurpose") +} + +func TestNewPermissionRequestForCreateWithoutConsentPurposeEnabled(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeTtl(365), + WithConsentPurposeChannel("web")) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Enabled is required for consentPurpose") +} + +func TestNewPermissionRequestForCreateWithoutConsentPurposeTtl(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + _, err := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + assert.NoError(t, err) +} + +func TestNewPermissionRequestForCreateWithoutCollectionMethod(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Collection Method is required for PermissionRequest") +} + +func TestNewPermissionRequestForCreateWithoutCollectionLocation(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithPolicyUrlForPermission("http://foo.baz"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Collection Location is required for PermissionRequest") +} + +func TestNewPermissionRequestForCreateWithoutPolicyUrl(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithTermsServiceForPermission("http://foo.tos")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Policy URL is required for PermissionRequest") +} + +func TestNewPermissionRequestForCreateWithoutTermsService(t *testing.T) { + consentPurposes := NewConsentPurpose( + WithConsentPurposeId(1), + WithConsentPurposeChannel("web"), + WithConsentPurposeTtl(365), + WithConsentPurposeEnabled(true)) + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithConsentPurposeForPermission(consentPurposes), + WithCollectionMethodForPermission("cookiePopUp"), + WithCollectionLocationForPermission("https://kenblahblah.com"), + WithPolicyUrlForPermission("http://foo.baz")) + err := validateForPermissionCreate(pr) + assert.EqualError(t, err, "FullContactError: Terms of Service is required for PermissionRequest") +} + +func TestNewPermissionRequestForVerifyWithPurposeIdAndChannel(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithPurposeIdForPermission(1), + WithChannelForPermission("email")) + err := validateForPermissionVerify(pr) + assert.NoError(t, err) +} + +func TestNewPermissionRequestForVerifyWithoutPurposeId(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithChannelForPermission("email")) + err := validateForPermissionVerify(pr) + assert.EqualError(t, err, "FullContactError: Purpose ID is required for PermissionRequest") +} + +func TestNewPermissionRequestForVerifyWithoutChannel(t *testing.T) { + multifieldRequest, _ := NewMultifieldRequest( + WithNameForMultifieldRequest(&PersonName{Given: "Marian", Family: "Reed"}), + WithLocationForMultifieldRequest(NewLocation( + WithAddressLine1("123/23"), + WithPostalCode("23432")))) + pr, _ := NewPermissionRequest( + WithMultifieldRequestForPermission(multifieldRequest), + WithPurposeIdForPermission(1)) + err := validateForPermissionVerify(pr) + assert.EqualError(t, err, "FullContactError: Channel is required for PermissionRequest") +} + +func TestNilPermissionCreateRequest(t *testing.T) { + fcTestClient := fullContactClient{} + ch := fcTestClient.PermissionCreate(nil) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.EqualError(t, resp.Err, "FullContactError: Permission Request can't be nil") +} + +func TestNilPermissionDeleteRequest(t *testing.T) { + fcTestClient := fullContactClient{} + ch := fcTestClient.PermissionDelete(nil) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.EqualError(t, resp.Err, "FullContactError: Multifield Request can't be nil") +} + +func TestNilPermissionFindRequest(t *testing.T) { + fcTestClient := fullContactClient{} + ch := fcTestClient.PermissionFind(nil) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.EqualError(t, resp.Err, "FullContactError: Multifield Request can't be nil") +} + +func TestNilPermissionCurrentRequest(t *testing.T) { + fcTestClient := fullContactClient{} + ch := fcTestClient.PermissionCurrent(nil) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.EqualError(t, resp.Err, "FullContactError: Multifield Request can't be nil") +} + +func TestNilPermissionVerifyRequest(t *testing.T) { + fcTestClient := fullContactClient{} + ch := fcTestClient.PermissionVerify(nil) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.EqualError(t, resp.Err, "FullContactError: Permission Request can't be nil") +} diff --git a/fc/permission_test.go b/fc/permission_test.go new file mode 100644 index 0000000..90e264b --- /dev/null +++ b/fc/permission_test.go @@ -0,0 +1,407 @@ +package fullcontact + +import ( + assert "github.com/stretchr/testify/require" + "testing" +) + +func TestPermissionCreate(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 202) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 202, resp.StatusCode) + assert.Equal(t, "202 Accepted", resp.Status) +} + +func TestPermissionDelete(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 200) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "200 OK", resp.Status) +} + +func TestPermissionCurrent(t *testing.T) { + ch := make(chan *APIResponse) + respJson := "{\"1\":{\"phone\":{\"ttl\":365,\"enabled\":true,\"channel\":\"phone\",\"purposeId\":1,\"purposeName\":\"Information storage & access\",\"timestamp\":1617962540547},\"web\":{\"ttl\":365,\"enabled\":true,\"channel\":\"web\",\"purposeId\":1,\"purposeName\":\"Information storage & access\",\"timestamp\":1617962540547}},\"2\":{\"mobile\":{\"ttl\":365,\"enabled\":true,\"channel\":\"mobile\",\"purposeId\":2,\"purposeName\":\"Personalized Ads Profile\",\"timestamp\":1617962540547}}}" + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, respJson, 200) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + response := resp.PermissionCurrentResponse + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "200 OK", resp.Status) + assert.Equal(t, 365, response["1"]["phone"].Ttl) + assert.Equal(t, true, response["1"]["phone"].Enabled) + assert.Equal(t, "phone", response["1"]["phone"].Channel) + assert.Equal(t, 1, response["1"]["phone"].PurposeId) + assert.Equal(t, "Information storage & access", response["1"]["phone"].PurposeName) + assert.Equal(t, 1617962540547, response["1"]["phone"].Timestamp) + assert.Equal(t, 365, response["1"]["web"].Ttl) + assert.Equal(t, true, response["1"]["web"].Enabled) + assert.Equal(t, "web", response["1"]["web"].Channel) + assert.Equal(t, 1, response["1"]["web"].PurposeId) + assert.Equal(t, "Information storage & access", response["1"]["web"].PurposeName) + assert.Equal(t, 1617962540547, response["1"]["web"].Timestamp) + assert.Equal(t, true, response["2"]["mobile"].Enabled) + assert.Equal(t, "mobile", response["2"]["mobile"].Channel) + assert.Equal(t, 2, response["2"]["mobile"].PurposeId) + assert.Equal(t, "Personalized Ads Profile", response["2"]["mobile"].PurposeName) + assert.Equal(t, 1617962540547, response["2"]["mobile"].Timestamp) +} + +func TestPermissionFind(t *testing.T) { + ch := make(chan *APIResponse) + respJson := "[{\"permissionType\":\"create\",\"permissionId\":\"1c99f4fb-96a2-46f4-8fd7-64750a591e05\",\"consentPurposes\":[{\"ttl\":365,\"enabled\":true,\"channel\":\"web\",\"purposeId\":1,\"purposeName\":\"Information storage & access\",\"timestamp\":1617628580297}],\"locale\":null,\"ipAddress\":null,\"language\":null,\"collectionMethod\":\"cookiePopUp\",\"collectionLocation\":\"https://kenblahblah.com\",\"policyUrl\":\"https://www.fullcontact.com/privacy/privacy-policy\",\"termsService\":\"https://www.fullcontact.com/privacy/terms-of-use\",\"timestamp\":null,\"created\":1617628580297}]" + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, respJson, 200) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + response := resp.PermissionFindResponse + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "200 OK", resp.Status) + assert.Equal(t, "create", response[0].PermissionType) + assert.Equal(t, "1c99f4fb-96a2-46f4-8fd7-64750a591e05", response[0].PermissionId) + assert.Equal(t, 365, response[0].ConsentPurposes[0].Ttl) + assert.Equal(t, true, response[0].ConsentPurposes[0].Enabled) + assert.Equal(t, "web", response[0].ConsentPurposes[0].Channel) + assert.Equal(t, 1, response[0].ConsentPurposes[0].PurposeId) + assert.Equal(t, "Information storage & access", response[0].ConsentPurposes[0].PurposeName) + assert.Equal(t, 1617628580297, response[0].ConsentPurposes[0].Timestamp) + assert.Equal(t, "", response[0].Locale) + assert.Equal(t, "", response[0].IpAddress) + assert.Equal(t, "", response[0].Language) + assert.Equal(t, "cookiePopUp", response[0].CollectionMethod) + assert.Equal(t, "https://kenblahblah.com", response[0].CollectionLocation) + assert.Equal(t, "https://www.fullcontact.com/privacy/privacy-policy", response[0].PolicyUrl) + assert.Equal(t, "https://www.fullcontact.com/privacy/terms-of-use", response[0].TermsService) + assert.Equal(t, 0, response[0].Timestamp) + assert.Equal(t, 1617628580297, response[0].Created) +} + +func TestPermissionVerify(t *testing.T) { + ch := make(chan *APIResponse) + respJson := "{\"ttl\":365,\"enabled\":true,\"channel\":\"web\",\"purposeId\":1,\"purposeName\":\"Information storage & access\",\"timestamp\":1617962540547}" + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, respJson, 200) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + response := resp.PermissionVerifyResponse + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 200, resp.StatusCode) + assert.Equal(t, "200 OK", resp.Status) + assert.Equal(t, 365, response.Ttl) + assert.Equal(t, true, response.Enabled) + assert.Equal(t, "web", response.Channel) + assert.Equal(t, 1, response.PurposeId) + assert.Equal(t, "Information storage & access", response.PurposeName) + assert.Equal(t, 1617962540547, response.Timestamp) +} + +func TestPermissionCreateStatus202(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 202) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 202, resp.StatusCode) +} + +func TestPermissionCreateStatus204(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 204) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 204, resp.StatusCode) +} + +func TestPermissionCreateStatus401(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 401) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 401, resp.StatusCode) +} + +func TestPermissionCreateStatus403(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 403) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 403, resp.StatusCode) +} + +func TestPermissionCreateStatus404(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 404) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 404, resp.StatusCode) +} + +func TestPermissionCreateStatus500(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCreateUrl, "", 500) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 500, resp.StatusCode) +} + +func TestPermissionDeleteStatus202(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 202) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 202, resp.StatusCode) +} + +func TestPermissionDeleteStatus204(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 204) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 204, resp.StatusCode) +} + +func TestPermissionDeleteStatus401(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 401) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 401, resp.StatusCode) +} + +func TestPermissionDeleteStatus403(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 403) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 403, resp.StatusCode) +} + +func TestPermissionDeleteStatus404(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 404) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 404, resp.StatusCode) +} + +func TestPermissionDeleteStatus500(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionDeleteUrl, "", 500) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 500, resp.StatusCode) +} + +func TestPermissionFindStatus202(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, "", 202) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 202, resp.StatusCode) +} + +func TestPermissionFindStatus204(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, "", 204) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 204, resp.StatusCode) +} + +func TestPermissionFindStatus401(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, "", 401) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 401, resp.StatusCode) +} + +func TestPermissionFindStatus403(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, "", 403) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 403, resp.StatusCode) +} + +func TestPermissionFindStatus404(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, "", 404) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 404, resp.StatusCode) +} + +func TestPermissionFindStatus500(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionFindUrl, "", 500) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 500, resp.StatusCode) +} + +func TestPermissionCurrentStatus202(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, "", 202) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 202, resp.StatusCode) +} + +func TestPermissionCurrentStatus204(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, "", 204) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 204, resp.StatusCode) +} + +func TestPermissionCurrentStatus401(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, "", 401) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 401, resp.StatusCode) +} + +func TestPermissionCurrentStatus403(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, "", 403) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 403, resp.StatusCode) +} + +func TestPermissionCurrentStatus404(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, "", 404) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 404, resp.StatusCode) +} + +func TestPermissionCurrentStatus500(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionCurrentUrl, "", 500) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 500, resp.StatusCode) +} + +func TestPermissionVerifyStatus202(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, "", 202) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 202, resp.StatusCode) +} + +func TestPermissionVerifyStatus204(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, "", 204) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 204, resp.StatusCode) +} + +func TestPermissionVerifyStatus401(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, "", 401) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 401, resp.StatusCode) +} + +func TestPermissionVerifyStatus403(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, "", 403) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 403, resp.StatusCode) +} + +func TestPermissionVerifyStatus404(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, "", 404) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.True(t, resp.IsSuccessful) + assert.Equal(t, 404, resp.StatusCode) +} + +func TestPermissionVerifyStatus500(t *testing.T) { + ch := make(chan *APIResponse) + fcTestClient, testServer := getTestServerAndClient(permissionVerifyUrl, "", 500) + defer testServer.Close() + go fcTestClient.do(testServer.URL, nil, ch) + resp := <-ch + assert.False(t, resp.IsSuccessful) + assert.Equal(t, 500, resp.StatusCode) +} diff --git a/fc/person_request.go b/fc/person_request.go index 8b28c4a..a7384aa 100644 --- a/fc/person_request.go +++ b/fc/person_request.go @@ -17,6 +17,7 @@ type PersonRequest struct { LiNonId string `json:"li_nonid,omitempty"` Confidence string `json:"confidence,omitempty"` Infer bool `json:"infer,omitempty"` + Placekey string `json:"placekey,omitempty"` } func NewPersonRequest(option ...PersonRequestOption) (*PersonRequest, error) { @@ -47,7 +48,7 @@ func validatePersonRequest(pr *PersonRequest) error { return NewFullContactError("Confidence value can only be 'LOW', 'MED', 'HIGH', 'MAX'") } if !pr.isQueryable() { - if pr.Location == nil && pr.Name == nil { + if (pr.Location == nil && pr.Name == nil && !isPopulated(pr.Placekey)) || (isPopulated(pr.Placekey) && pr.Name != nil ){ return nil } else if pr.Location != nil && pr.Name != nil { // Validating Location fields @@ -68,7 +69,7 @@ func validatePersonRequest(pr *PersonRequest) error { } } return NewFullContactError( - "If you want to use 'location' or 'name' as an input, both must be present and they must have non-blank values") + "If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") } return nil } @@ -216,3 +217,9 @@ func WithInfer(infer bool) PersonRequestOption { pr.Infer = infer } } + +func WithPlacekey(placekey string) PersonRequestOption { + return func(pr *PersonRequest) { + pr.Placekey = placekey + } +} diff --git a/fc/person_request_test.go b/fc/person_request_test.go index 242e10d..072c7e6 100644 --- a/fc/person_request_test.go +++ b/fc/person_request_test.go @@ -63,7 +63,7 @@ func TestNewPersonRequestWithNameOnlyWithoutQueryable(t *testing.T) { Full: "Marian C Reed", })) err := validatePersonRequest(pr) - assert.EqualError(t, err, "FullContactError: If you want to use 'location' or 'name' as an input, both must be present and they must have non-blank values") + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") } func TestNewPersonRequestWithLocationOnlyWithQueryable(t *testing.T) { @@ -90,7 +90,7 @@ func TestNewPersonRequestWithLocationOnlyWithoutQueryable(t *testing.T) { WithRegionCode("123123"), WithPostalCode("23124")))) err := validatePersonRequest(pr) - assert.EqualError(t, err, "FullContactError: If you want to use 'location' or 'name' as an input, both must be present and they must have non-blank values") + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") } func TestNewPersonRequestWithLocationWithoutAddressLine1WithQueryable(t *testing.T) { @@ -214,6 +214,21 @@ func TestNewPersonRequestWithValidLocation3(t *testing.T) { assert.NoError(t, err) } +func TestNewPersonRequestWithValidNameWithPlacekey(t *testing.T) { + pr, _ := NewPersonRequest( + WithName(&PersonName{Full: "Marian C Reed"}), + WithPlacekey("226@5z4-zvy-ffz")) + err := validatePersonRequest(pr) + assert.NoError(t, err) +} + +func TestNewPersonRequestWithPlacekeyOnly(t *testing.T) { + pr, _ := NewPersonRequest( + WithPlacekey("226@5z4-zvy-ffz")) + err := validatePersonRequest(pr) + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") +} + func TestNewPersonRequestWithValidName(t *testing.T) { pr, _ := NewPersonRequest( WithName(&PersonName{Given: "Marian", Family: "Reed"}), diff --git a/fc/resolve_request.go b/fc/resolve_request.go index 0d83564..821a6a0 100644 --- a/fc/resolve_request.go +++ b/fc/resolve_request.go @@ -14,6 +14,7 @@ type ResolveRequest struct { PartnerId string `json:"partnerId,omitempty"` LiNonId string `json:"li_nonid,omitempty"` Tags []*Tag `json:"tags,omitempty"` + Placekey string `json:"placekey,omitempty"` } func NewResolveRequest(option ...ResolveRequestOption) (*ResolveRequest, error) { @@ -36,7 +37,9 @@ func (resolveRequest *ResolveRequest) isQueryable() bool { func validateResolveRequest(resolveRequest *ResolveRequest) error { if !resolveRequest.isQueryable() { - if resolveRequest.Location == nil && resolveRequest.Name == nil { + if resolveRequest.Location == nil && resolveRequest.Name == nil && !isPopulated(resolveRequest.Placekey){ + return nil + } else if isPopulated(resolveRequest.Placekey) && resolveRequest.Name != nil { return nil } else if resolveRequest.Location != nil && resolveRequest.Name != nil { // Validating Location fields @@ -57,7 +60,7 @@ func validateResolveRequest(resolveRequest *ResolveRequest) error { } } return NewFullContactError( - "If you want to use 'location' or 'name' as an input, both must be present and they must have non-blank values") + "If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") } return nil } @@ -219,3 +222,9 @@ func WithTagsForResolve(tags []*Tag) ResolveRequestOption { resolveRequest.Tags = append(resolveRequest.Tags, tags...) } } + +func WithPlacekeyForResolve(placekey string) ResolveRequestOption { + return func(resolveRequest *ResolveRequest) { + resolveRequest.Placekey = placekey + } +} diff --git a/fc/resolve_request_test.go b/fc/resolve_request_test.go index 489daee..2f6a4bc 100644 --- a/fc/resolve_request_test.go +++ b/fc/resolve_request_test.go @@ -58,7 +58,7 @@ func TestNewResolveRequestWithNameOnlyWithoutQueryable(t *testing.T) { Full: "Marian C Reed", })) err := validateResolveRequest(resolveRequest) - assert.EqualError(t, err, "FullContactError: If you want to use 'location' or 'name' as an input, both must be present and they must have non-blank values") + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") } func TestNewResolveRequestWithLocationOnlyWithQueryable(t *testing.T) { @@ -85,7 +85,7 @@ func TestNewResolveRequestWithLocationOnlyWithoutQueryable(t *testing.T) { WithRegionCode("123123"), WithPostalCode("23124")))) err := validateResolveRequest(resolveRequest) - assert.EqualError(t, err, "FullContactError: If you want to use 'location' or 'name' as an input, both must be present and they must have non-blank values") + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") } func TestNewResolveRequestWithLocationWithoutAddressLine1WithQueryable(t *testing.T) { @@ -209,6 +209,21 @@ func TestNewResolveRequestWithValidLocation3(t *testing.T) { assert.NoError(t, err) } +func TestNewResolveRequestWithValidNameWithPlacekey(t *testing.T) { + resolveRequest, _ := NewResolveRequest( + WithNameForResolve(&PersonName{Full: "Marian C Reed"}), + WithPlacekeyForResolve("226@5z4-zvy-ffz")) + err := validateResolveRequest(resolveRequest) + assert.NoError(t, err) +} + +func TestNewResolveRequestWithPlacekeyOnly(t *testing.T) { + resolveRequest, _ := NewResolveRequest( + WithPlacekeyForResolve("226@5z4-zvy-ffz")) + err := validateResolveRequest(resolveRequest) + assert.EqualError(t, err, "FullContactError: If you want to use 'location'(or placekey) or 'name' as an input, both must be present and they must have non-blank values") +} + func TestNewResolveRequestWithValidName(t *testing.T) { resolveRequest, _ := NewResolveRequest( WithNameForResolve(&PersonName{Given: "Marian", Family: "Reed"}),