From 61debccbc090e8946c60a8d5afe939f689b5437d Mon Sep 17 00:00:00 2001 From: echarrod Date: Thu, 8 Nov 2018 19:13:57 +0000 Subject: [PATCH 1/8] [SDK-443]: Update example project, vscode instructions --- CONTRIBUTING.md | 2 +- examples/aml/main.go | 7 ++-- examples/profile/main.go | 61 +++++++++++++++++++++++------------ examples/profile/profile.html | 36 +++++++++------------ 4 files changed, 60 insertions(+), 46 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0551b633..ccae2763 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,7 @@ For developing in VS Code, use the following `launch.json` file (placed inside a "host": "127.0.0.1", "program": "${workspaceFolder}/examples/profile/main.go", "env": {}, - "args": [], + "args": ["certificatehelper.go"], "showLog": true } ] diff --git a/examples/aml/main.go b/examples/aml/main.go index 09177b03..6e1d1776 100644 --- a/examples/aml/main.go +++ b/examples/aml/main.go @@ -18,15 +18,16 @@ var ( ) func main() { - var sdkID = os.Getenv("YOTI_CLIENT_SDK_ID") - var key, err = ioutil.ReadFile(os.Getenv("YOTI_KEY_FILE_PATH")) + var err error + key, err = ioutil.ReadFile(os.Getenv("YOTI_KEY_FILE_PATH")) + sdkID = os.Getenv("YOTI_CLIENT_SDK_ID") if err != nil { log.Printf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: `%s`", err) return } - var client = yoti.Client{ + client = &yoti.Client{ SdkID: sdkID, Key: key} diff --git a/examples/profile/main.go b/examples/profile/main.go index 47464450..94bf8dd9 100644 --- a/examples/profile/main.go +++ b/examples/profile/main.go @@ -6,6 +6,7 @@ import ( "html/template" "image" "image/jpeg" + "io" "io/ioutil" "log" "net/http" @@ -36,40 +37,58 @@ func home(w http.ResponseWriter, req *http.Request) { } func profile(w http.ResponseWriter, r *http.Request) { - var sdkID = os.Getenv("YOTI_CLIENT_SDK_ID") - var key, err = ioutil.ReadFile(os.Getenv("YOTI_KEY_FILE_PATH")) + var err error + key, err = ioutil.ReadFile(os.Getenv("YOTI_KEY_FILE_PATH")) + sdkID = os.Getenv("YOTI_CLIENT_SDK_ID") if err != nil { - log.Printf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: `%s`", err) - return + log.Fatalf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: `%s`", err) } - var client = yoti.Client{ + client = &yoti.Client{ SdkID: sdkID, Key: key} yotiOneTimeUseToken := r.URL.Query().Get("token") - profile, err := client.GetUserProfile(yotiOneTimeUseToken) - if err == nil { - templateVars := map[string]interface{}{ - "profile": profile, - "selfieBase64URL": template.URL(profile.Selfie.URL())} + activityDetails, errStrings := client.GetActivityDetails(yotiOneTimeUseToken) + if len(errStrings) != 0 { + log.Fatalf("Errors: %v", errStrings) + } - decodedImage := decodeImage(profile.Selfie.Data) - file := createImage() - saveImage(decodedImage, file) + userProfile := activityDetails.UserProfile - t, err := template.ParseFiles("profile.html") - if err != nil { - fmt.Println(err) - return - } + var base64URL string + base64URL, err = userProfile.Selfie().Base64URL() + + if err != nil { + log.Fatalf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: %q", err) + } + + dob, err := userProfile.DateOfBirth() + if err != nil { + log.Fatalf("Error parsing Date of Birth attribute. Error %q", err) + } + + templateVars := map[string]interface{}{ + "profile": userProfile, + "selfieBase64URL": template.URL(base64URL), + "rememberMeID": activityDetails.RememberMeID, + "dateOfBirth": dob, + } + + decodedImage := decodeImage(userProfile.Selfie().Value) + file := createImage() + saveImage(decodedImage, file) - t.Execute(w, templateVars) - } else { + var t *template.Template + t, err = template.ParseFiles("profile.html") + if err != nil { fmt.Println(err) + return } + + t.Execute(w, templateVars) } func main() { @@ -131,7 +150,7 @@ func createImage() (file *os.File) { return } -func saveImage(img image.Image, file *os.File) { +func saveImage(img image.Image, file io.Writer) { var opt jpeg.Options opt.Quality = 100 diff --git a/examples/profile/profile.html b/examples/profile/profile.html index e45fa604..be93768d 100644 --- a/examples/profile/profile.html +++ b/examples/profile/profile.html @@ -8,8 +8,8 @@

Home

- - + + {{if .selfieBase64URL}} @@ -24,61 +24,55 @@

Home

{{if .profile.GivenNames}} - + {{end}} - {{if .profile.FamilyName}} + {{if .profile.FamilyName.Value}} - + {{end}} {{if .profile.FullName}} - + {{end}} {{if .profile.MobileNumber}} - + {{end}} {{if .profile.EmailAddress}} - + {{end}} - {{if .profile.DateOfBirth}} + {{if .dateOfBirth}} - + - {{end}} - {{if .profile.IsAgeVerified}} - - - - - {{end}} + {{end}} {{if .profile.Address}} - + {{end}} {{if .profile.Gender}} - + {{end}} {{if .profile.Nationality}} - + {{end}} {{if .profile.StructuredPostalAddress}} @@ -86,7 +80,7 @@

Home

ID:{{.profile.ID}}Remember Me ID:{{.rememberMeID}}
First Name:{{.profile.GivenNames}}{{.profile.GivenNames.Value}}
Family Name:{{.profile.FamilyName}}{{.profile.FamilyName.Value}}
Full Name:{{.profile.FullName}}{{.profile.FullName.Value}}
Mobile Number:{{.profile.MobileNumber}}{{.profile.MobileNumber.Value}}
Email Address:{{.profile.EmailAddress}}{{.profile.EmailAddress.Value}}
Date of Birth:{{.profile.DateOfBirth}}{{.dateOfBirth.Value}}
Is Age Verified:{{.profile.IsAgeVerified}}
Address:{{.profile.Address}}{{.profile.Address.Value}}
Gender:{{.profile.Gender}}{{.profile.Gender.Value}}
Nationality:{{.profile.Nationality}}{{.profile.Nationality.Value}}
Structured Postal Address: - {{range $key, $value := .profile.StructuredPostalAddress}} + {{range $key, $value := .profile.StructuredPostalAddress.Value}} From 3206be386a89ae47f4cb918a2b2e396e027bfb50 Mon Sep 17 00:00:00 2001 From: echarrod Date: Fri, 9 Nov 2018 16:00:07 +0000 Subject: [PATCH 2/8] [SDK-443]: Add sources & verifiers --- README.md | 20 ++++++++++++++- anchor/anchors.go | 23 +++++++++++++++-- attribute/genericattribute.go | 14 ++++++++--- attribute/image.go | 15 ++++++----- attribute/imageattribute.go | 12 ++++++--- attribute/jsonattribute.go | 10 ++++++-- attribute/stringattribute.go | 14 ++++++++--- attribute/timeattribute.go | 14 ++++++++--- examples/profile/main.go | 4 +-- yoti_test.go | 47 ++++++++++++++++++++++++++++++++++- yoticlient.go | 2 +- 11 files changed, 145 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 379788ac..31cdd47b 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,24 @@ userProfile.GetAttribute("age_over:18").Value.(string) GetAttribute returns an interface, the value can be acquired through a type assertion. + +You can retrieve the anchors, sources and verifiers for each attribute as follows: +```Go +givenNamesAnchors := userProfile.GivenNames().Anchors +givenNamesSources := userProfile.GivenNames().Sources +givenNamesVerifiers := userProfile.GivenNames().Verifiers +``` +You can also retrieve further properties from these respective anchors in the following way: +```Go +givenNamesFirstAnchor := userProfile.GivenNames().Anchors[0] + +anchorType := givenNamesFirstAnchor.Type +originServerCerts := givenNamesFirstAnchor.OriginServerCerts() +signedTimestamp := givenNamesFirstAnchor.SignedTimestamp().Timestamp +subType := givenNamesFirstAnchor.SubType() +value := givenNamesFirstAnchor.Value() +``` + ## Handling Users When you retrieve the user profile, you receive a user ID generated by Yoti exclusively for your application. @@ -200,7 +218,7 @@ Performing an AML check on a person *requires* their consent. Given a YotiClient initialised with your SDK ID and KeyPair (see [Client Initialisation](#client-initialisation)) performing an AML check is a straightforward case of providing basic profile data. -```go +```Go givenNames := "Edward Richard George" familyName := "Heath" diff --git a/anchor/anchors.go b/anchor/anchors.go index e73cb892..0b0ad62d 100644 --- a/anchor/anchors.go +++ b/anchor/anchors.go @@ -57,8 +57,8 @@ func (a Anchor) OriginServerCerts() []*x509.Certificate { // message associated with the timestamp is the marshaled form of // AttributeSigning (i.e. the same message that is signed in the // Signature field). This method returns the SignedTimestamp -// object, the actual timestamp as a DateTime can be called with -// .Timestamp() on this object +// object, the actual timestamp as a *time.Time can be called with +// .Timestamp on the result of this function func (a Anchor) SignedTimestamp() SignedTimestamp { return a.signedTimestamp } @@ -76,3 +76,22 @@ func (a Anchor) SubType() string { func (a Anchor) Value() []string { return a.value } + +// GetSources returns the anchors which identify how and when an attribute value was acquired. +func GetSources(anchors []*Anchor) (sources []*Anchor) { + return filterAnchors(anchors, AnchorTypeSource) +} + +// GetVerifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func GetVerifiers(anchors []*Anchor) (sources []*Anchor) { + return filterAnchors(anchors, AnchorTypeVerifier) +} + +func filterAnchors(anchors []*Anchor, anchorType Type) (result []*Anchor) { + for _, v := range anchors { + if v.Type == anchorType { + result = append(result, v) + } + } + return result +} diff --git a/attribute/genericattribute.go b/attribute/genericattribute.go index 9caa27b7..5db7801c 100644 --- a/attribute/genericattribute.go +++ b/attribute/genericattribute.go @@ -11,8 +11,10 @@ import ( //GenericAttribute is a Yoti attribute which returns a generic value type GenericAttribute struct { *yotiprotoattr_v3.Attribute - Value interface{} - Anchors []*anchor.Anchor + Value interface{} + Anchors []*anchor.Anchor + Sources []*anchor.Anchor + Verifiers []*anchor.Anchor } //NewGeneric creates a new generic attribute @@ -49,12 +51,16 @@ func NewGeneric(a *yotiprotoattr_v3.Attribute) *GenericAttribute { value = a.Value } + parsedAnchors := anchor.ParseAnchors(a.Anchors) + return &GenericAttribute{ Attribute: &yotiprotoattr_v3.Attribute{ Name: a.Name, ContentType: a.ContentType, }, - Value: value, - Anchors: anchor.ParseAnchors(a.Anchors), + Value: value, + Anchors: parsedAnchors, + Sources: anchor.GetSources(parsedAnchors), + Verifiers: anchor.GetVerifiers(parsedAnchors), } } diff --git a/attribute/image.go b/attribute/image.go index 8ff1b9c8..b1cbbee2 100644 --- a/attribute/image.go +++ b/attribute/image.go @@ -2,7 +2,7 @@ package attribute import ( "encoding/base64" - "log" + "fmt" ) //ImageType Image format @@ -30,19 +30,22 @@ var mimeTypeMap = map[ImageType]string{ // GetMIMEType returns the MIME type of this piece of Yoti user information. For more information see: // https://en.wikipedia.org/wiki/Media_type -func GetMIMEType(imageType ImageType) (result string) { +func GetMIMEType(imageType ImageType) (string, error) { if val, ok := mimeTypeMap[imageType]; ok { - return val + return val, nil } - log.Printf("Unable to find a matching MIME type for value type %q", imageType) - return + return "", fmt.Errorf("Unable to find a matching MIME type for value type %q", imageType) } // Base64URL is the Image encoded as a base64 URL func (image *Image) Base64URL() (string, error) { base64EncodedImage := base64.StdEncoding.EncodeToString(image.Data) - contentType := GetMIMEType(image.Type) + contentType, err := GetMIMEType(image.Type) + + if err != nil { + return "", err + } return "data:" + contentType + ";base64;," + base64EncodedImage, nil } diff --git a/attribute/imageattribute.go b/attribute/imageattribute.go index 701d03a4..6839668a 100644 --- a/attribute/imageattribute.go +++ b/attribute/imageattribute.go @@ -8,8 +8,10 @@ import ( //ImageAttribute is a Yoti attribute which returns an image as its value type ImageAttribute struct { *yotiprotoattr_v3.Attribute - Value *Image - Anchors []*anchor.Anchor + Value *Image + Anchors []*anchor.Anchor + Sources []*anchor.Anchor + Verifiers []*anchor.Anchor } //NewImage creates a new Image attribute @@ -27,6 +29,8 @@ func NewImage(a *yotiprotoattr_v3.Attribute) *ImageAttribute { imageType = ImageTypeOther } + parsedAnchors := anchor.ParseAnchors(a.Anchors) + return &ImageAttribute{ Attribute: &yotiprotoattr_v3.Attribute{ Name: a.Name, @@ -36,6 +40,8 @@ func NewImage(a *yotiprotoattr_v3.Attribute) *ImageAttribute { Data: a.Value, Type: imageType, }, - Anchors: anchor.ParseAnchors(a.Anchors), + Anchors: parsedAnchors, + Sources: anchor.GetSources(parsedAnchors), + Verifiers: anchor.GetVerifiers(parsedAnchors), } } diff --git a/attribute/jsonattribute.go b/attribute/jsonattribute.go index 3685fbe3..b3ad6fd9 100644 --- a/attribute/jsonattribute.go +++ b/attribute/jsonattribute.go @@ -13,6 +13,8 @@ type JSONAttribute struct { *yotiprotoattr_v3.Attribute // Value returns the value of a JSON attribute in the form of an interface Value interface{} Anchors []*anchor.Anchor + Sources []*anchor.Anchor + Verifiers []*anchor.Anchor } //NewJSON creates a new JSON attribute @@ -23,13 +25,17 @@ func NewJSON(a *yotiprotoattr_v3.Attribute) (*JSONAttribute, error) { return nil, err } + parsedAnchors := anchor.ParseAnchors(a.Anchors) + return &JSONAttribute{ Attribute: &yotiprotoattr_v3.Attribute{ Name: a.Name, ContentType: a.ContentType, }, - Value: interfaceValue, - Anchors: anchor.ParseAnchors(a.Anchors), + Value: interfaceValue, + Anchors: parsedAnchors, + Sources: anchor.GetSources(parsedAnchors), + Verifiers: anchor.GetVerifiers(parsedAnchors), }, nil } diff --git a/attribute/stringattribute.go b/attribute/stringattribute.go index e128a031..f86531db 100644 --- a/attribute/stringattribute.go +++ b/attribute/stringattribute.go @@ -8,18 +8,24 @@ import ( //StringAttribute is a Yoti attribute which returns a string as its value type StringAttribute struct { *yotiprotoattr_v3.Attribute - Value string - Anchors []*anchor.Anchor + Value string + Anchors []*anchor.Anchor + Sources []*anchor.Anchor + Verifiers []*anchor.Anchor } //NewString creates a new String attribute func NewString(a *yotiprotoattr_v3.Attribute) *StringAttribute { + parsedAnchors := anchor.ParseAnchors(a.Anchors) + return &StringAttribute{ Attribute: &yotiprotoattr_v3.Attribute{ Name: a.Name, ContentType: a.ContentType, }, - Value: string(a.Value), - Anchors: anchor.ParseAnchors(a.Anchors), + Value: string(a.Value), + Anchors: parsedAnchors, + Sources: anchor.GetSources(parsedAnchors), + Verifiers: anchor.GetVerifiers(parsedAnchors), } } diff --git a/attribute/timeattribute.go b/attribute/timeattribute.go index 40b2bada..2bbfa9aa 100644 --- a/attribute/timeattribute.go +++ b/attribute/timeattribute.go @@ -11,8 +11,10 @@ import ( //TimeAttribute is a Yoti attribute which returns a time as its value type TimeAttribute struct { *yotiprotoattr_v3.Attribute - Value *time.Time - Anchors []*anchor.Anchor + Value *time.Time + Anchors []*anchor.Anchor + Sources []*anchor.Anchor + Verifiers []*anchor.Anchor } //NewTime creates a new Time attribute @@ -24,12 +26,16 @@ func NewTime(a *yotiprotoattr_v3.Attribute) (*TimeAttribute, error) { return nil, err } + parsedAnchors := anchor.ParseAnchors(a.Anchors) + return &TimeAttribute{ Attribute: &yotiprotoattr_v3.Attribute{ Name: a.Name, ContentType: a.ContentType, }, - Value: &parsedTime, - Anchors: anchor.ParseAnchors(a.Anchors), + Value: &parsedTime, + Anchors: parsedAnchors, + Sources: anchor.GetSources(parsedAnchors), + Verifiers: anchor.GetVerifiers(parsedAnchors), }, nil } diff --git a/examples/profile/main.go b/examples/profile/main.go index 94bf8dd9..c00dc660 100644 --- a/examples/profile/main.go +++ b/examples/profile/main.go @@ -59,7 +59,7 @@ func profile(w http.ResponseWriter, r *http.Request) { userProfile := activityDetails.UserProfile var base64URL string - base64URL, err = userProfile.Selfie().Base64URL() + base64URL, err = userProfile.Selfie().Value.Base64URL() if err != nil { log.Fatalf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: %q", err) @@ -77,7 +77,7 @@ func profile(w http.ResponseWriter, r *http.Request) { "dateOfBirth": dob, } - decodedImage := decodeImage(userProfile.Selfie().Value) + decodedImage := decodeImage(userProfile.Selfie().Value.Data) file := createImage() saveImage(decodedImage, file) diff --git a/yoti_test.go b/yoti_test.go index 0102aad8..985a8630 100644 --- a/yoti_test.go +++ b/yoti_test.go @@ -908,6 +908,10 @@ func TestAnchorParser_Passport(t *testing.T) { actualAnchor := actualStructuredPostalAddress.Anchors[0] + if actualAnchor != actualStructuredPostalAddress.Sources[0] { + t.Error("Anchors and Sources should be the same when there is only one Source") + } + if actualAnchor.Type != anchor.AnchorTypeSource { t.Errorf("Parsed anchor type is incorrect. Expected: %q, actual: %q", anchor.AnchorTypeSource, actualAnchor.Type) } @@ -944,7 +948,12 @@ func TestAnchorParser_DrivingLicense(t *testing.T) { result := createProfileWithSingleAttribute(attribute) - resultAnchor := result.Gender().Anchors[0] + genderAttribute := result.Gender() + resultAnchor := genderAttribute.Anchors[0] + + if resultAnchor != genderAttribute.Sources[0] { + t.Error("Anchors and Sources should be the same when there is only one Source") + } if resultAnchor.Type != anchor.AnchorTypeSource { t.Errorf("Parsed anchor type is incorrect. Expected: %q, actual: %q", anchor.AnchorTypeSource, resultAnchor.Type) @@ -989,6 +998,10 @@ func TestAnchorParser_YotiAdmin(t *testing.T) { resultAnchor := DoB.Anchors[0] + if resultAnchor != DoB.Verifiers[0] { + t.Error("Anchors and Verifiers should be the same when there is only one Verifier") + } + if resultAnchor.Type != anchor.AnchorTypeVerifier { t.Errorf("Parsed anchor type is incorrect. Expected: %q, actual: %q", anchor.AnchorTypeVerifier, resultAnchor.Type) } @@ -1013,6 +1026,38 @@ func TestAnchorParser_YotiAdmin(t *testing.T) { AssertServerCertSerialNo(t, "256616937783084706710155170893983549581", actualSerialNo) } +func TestAnchors_Unknown(t *testing.T) { + unknownAnchor := &anchor.Anchor{ + Type: anchor.AnchorTypeUnknown, + } + + anchorSlice := append([]*anchor.Anchor{}, unknownAnchor) + + sources := anchor.GetSources(anchorSlice) + if len(sources) > 0 { + t.Error("Unknown anchor should not be returned by GetSources") + } + + verifiers := anchor.GetVerifiers(anchorSlice) + if len(verifiers) > 0 { + t.Error("Unknown anchor should not be returned by GetVerifiers") + } +} + +func TestAnchors_None(t *testing.T) { + anchorSlice := []*anchor.Anchor{} + + sources := anchor.GetSources(anchorSlice) + if len(sources) > 0 { + t.Error("GetSources should not return anything with empty anchors") + } + + verifiers := anchor.GetVerifiers(anchorSlice) + if len(verifiers) > 0 { + t.Error("GetVerifiers should not return anything with empty anchors") + } +} + func createProfileWithSingleAttribute(attr *yotiprotoattr_v3.Attribute) Profile { var attributeSlice []*yotiprotoattr_v3.Attribute attributeSlice = append(attributeSlice, attr) diff --git a/yoticlient.go b/yoticlient.go index 951abe06..39e80d6b 100644 --- a/yoticlient.go +++ b/yoticlient.go @@ -51,7 +51,7 @@ type ActivityDetails struct { RememberMeID string } -// Deprecated: Will be removed in v3.0.0. Use `GetProfile` instead. GetUserProfile requests information about a Yoti user using the one time use token generated by the Yoti login process. +// Deprecated: Will be removed in v3.0.0. Use `GetActivityDetails` instead. GetUserProfile requests information about a Yoti user using the one time use token generated by the Yoti login process. // It returns the outcome of the request. If the request was successful it will include the users details, otherwise // it will specify a reason the request failed. func (client *Client) GetUserProfile(token string) (userProfile UserProfile, firstError error) { From 1aa3f5db7fe3fee7cc7055c03065f03a4899f10e Mon Sep 17 00:00:00 2001 From: echarrod Date: Fri, 9 Nov 2018 16:01:13 +0000 Subject: [PATCH 3/8] [SDK-633]: Update browser.js to 2.2.0 --- examples/profile/login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/profile/login.html b/examples/profile/login.html index 0e689c8a..bd3d4523 100644 --- a/examples/profile/login.html +++ b/examples/profile/login.html @@ -3,7 +3,7 @@ Yoti Example Project - + Use Yoti From a91344012e6eeffadd73535a8629579d78036048 Mon Sep 17 00:00:00 2001 From: echarrod Date: Mon, 12 Nov 2018 12:10:44 +0000 Subject: [PATCH 4/8] [SDK-443]: Address PR comments: update README, make attributeSlice unexported, change image type to be string --- .gitignore | 3 +++ README.md | 22 +++++++++++++++++++-- attribute/image.go | 30 ++++++----------------------- attribute/imageattribute.go | 10 ++++++---- yoti_test.go | 4 ++-- yoticlient.go | 6 +++--- yotiprofile.go | 38 ++++++++++++++++++++----------------- 7 files changed, 61 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index b8687077..b79bd93d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ examples/profile/images/YotiSelfie.jpeg # Example project generated self-signed certificate examples/profile/yotiSelfSignedCert.pem examples/profile/yotiSelfSignedKey.pem + +# Debug files +debug \ No newline at end of file diff --git a/README.md b/README.md index 31cdd47b..6de373ae 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,8 @@ if len(errStrings) != 0 { } ``` +### Profile + You can then get the user profile from the activityDetails struct: ```Go @@ -135,19 +137,35 @@ userProfile.GetAttribute("age_over:18").Value.(string) GetAttribute returns an interface, the value can be acquired through a type assertion. +### Anchors, Sources and Verifiers + +An `Anchor` represents how a given Attribute has been _sourced_ or _verified_. These values are created and signed whenever a Profile Attribute is created, or verified with an external party. + +For example, an attribute value that was _sourced_ from a Passport might have the following values: + +`Anchor` property | Example value +-----|------ +type | SOURCE +value | PASSPORT +subType | OCR +signedTimestamp | 2017-10-31, 19:45:59.123789 + +Similarly, an attribute _verified_ against the data held by an external party will have an `Anchor` of type _VERIFIER_, naming the party that verified it. + +From each attribute can retrieve the `Anchors`, and subsets `Sources` and `Verifiers` (all as `[]*anchor.Anchor`) as follows: -You can retrieve the anchors, sources and verifiers for each attribute as follows: ```Go givenNamesAnchors := userProfile.GivenNames().Anchors givenNamesSources := userProfile.GivenNames().Sources givenNamesVerifiers := userProfile.GivenNames().Verifiers ``` + You can also retrieve further properties from these respective anchors in the following way: + ```Go givenNamesFirstAnchor := userProfile.GivenNames().Anchors[0] anchorType := givenNamesFirstAnchor.Type -originServerCerts := givenNamesFirstAnchor.OriginServerCerts() signedTimestamp := givenNamesFirstAnchor.SignedTimestamp().Timestamp subType := givenNamesFirstAnchor.SubType() value := givenNamesFirstAnchor.Value() diff --git a/attribute/image.go b/attribute/image.go index b1cbbee2..e615e411 100644 --- a/attribute/image.go +++ b/attribute/image.go @@ -5,47 +5,29 @@ import ( "fmt" ) -//ImageType Image format -type ImageType int - const ( //ImageTypeJpeg JPEG format - ImageTypeJpeg ImageType = 1 + iota + ImageTypeJpeg string = "jpeg" //ImageTypePng PNG format - ImageTypePng - //ImageTypeOther Other image formats - ImageTypeOther + ImageTypePng string = "png" ) //Image format of the image and the image data type Image struct { - Type ImageType + Type string Data []byte } -var mimeTypeMap = map[ImageType]string{ - ImageTypeJpeg: "image/jpeg", - ImageTypePng: "image/png", -} - // GetMIMEType returns the MIME type of this piece of Yoti user information. For more information see: // https://en.wikipedia.org/wiki/Media_type -func GetMIMEType(imageType ImageType) (string, error) { - if val, ok := mimeTypeMap[imageType]; ok { - return val, nil - } - - return "", fmt.Errorf("Unable to find a matching MIME type for value type %q", imageType) +func GetMIMEType(imageType string) string { + return fmt.Sprintf("image/%v", imageType) } // Base64URL is the Image encoded as a base64 URL func (image *Image) Base64URL() (string, error) { base64EncodedImage := base64.StdEncoding.EncodeToString(image.Data) - contentType, err := GetMIMEType(image.Type) - - if err != nil { - return "", err - } + contentType := GetMIMEType(image.Type) return "data:" + contentType + ";base64;," + base64EncodedImage, nil } diff --git a/attribute/imageattribute.go b/attribute/imageattribute.go index 6839668a..46f81c16 100644 --- a/attribute/imageattribute.go +++ b/attribute/imageattribute.go @@ -1,6 +1,8 @@ package attribute import ( + "errors" + "github.com/getyoti/yoti-go-sdk/anchor" "github.com/getyoti/yoti-go-sdk/yotiprotoattr_v3" ) @@ -15,8 +17,8 @@ type ImageAttribute struct { } //NewImage creates a new Image attribute -func NewImage(a *yotiprotoattr_v3.Attribute) *ImageAttribute { - var imageType ImageType +func NewImage(a *yotiprotoattr_v3.Attribute) (*ImageAttribute, error) { + var imageType string switch a.ContentType { case yotiprotoattr_v3.ContentType_JPEG: @@ -26,7 +28,7 @@ func NewImage(a *yotiprotoattr_v3.Attribute) *ImageAttribute { imageType = ImageTypePng default: - imageType = ImageTypeOther + return nil, errors.New("Cannot create ImageAttribute with unsupported type") } parsedAnchors := anchor.ParseAnchors(a.Anchors) @@ -43,5 +45,5 @@ func NewImage(a *yotiprotoattr_v3.Attribute) *ImageAttribute { Anchors: parsedAnchors, Sources: anchor.GetSources(parsedAnchors), Verifiers: anchor.GetVerifiers(parsedAnchors), - } + }, nil } diff --git a/yoti_test.go b/yoti_test.go index 985a8630..681a9106 100644 --- a/yoti_test.go +++ b/yoti_test.go @@ -706,7 +706,7 @@ func TestProfile_GetAttribute_Undefined(t *testing.T) { } func TestProfile_GetAttribute_ReturnsNil(t *testing.T) { result := Profile{ - AttributeSlice: []*yotiprotoattr_v3.Attribute{}, + attributeSlice: []*yotiprotoattr_v3.Attribute{}, } attribute := result.GetAttribute("attributeName") @@ -1063,7 +1063,7 @@ func createProfileWithSingleAttribute(attr *yotiprotoattr_v3.Attribute) Profile attributeSlice = append(attributeSlice, attr) return Profile{ - AttributeSlice: attributeSlice, + attributeSlice: attributeSlice, } } diff --git a/yoticlient.go b/yoticlient.go index 39e80d6b..b07fb060 100644 --- a/yoticlient.go +++ b/yoticlient.go @@ -135,7 +135,7 @@ func getActivityDetails(requester httpRequester, encryptedToken, sdkID string, k attributeSlice := createAttributeSlice(id, attributeList) profile := Profile{ - AttributeSlice: attributeSlice, + attributeSlice: attributeSlice, } var formattedAddress string @@ -157,7 +157,7 @@ func getActivityDetails(requester httpRequester, encryptedToken, sdkID string, k Anchors: protoStructuredPostalAddress.Anchors, } - profile.AttributeSlice = append(profile.AttributeSlice, addressAttribute) + profile.attributeSlice = append(profile.attributeSlice, addressAttribute) } activityDetails = ActivityDetails{ @@ -182,7 +182,7 @@ func getActivityDetails(requester httpRequester, encryptedToken, sdkID string, k } func getProtobufAttribute(profile Profile, key string) *yotiprotoattr_v3.Attribute { - for _, v := range profile.AttributeSlice { + for _, v := range profile.attributeSlice { if v.Name == attrConstStructuredPostalAddress { return v } diff --git a/yotiprofile.go b/yotiprofile.go index dec11841..d129e23f 100644 --- a/yotiprofile.go +++ b/yotiprofile.go @@ -19,18 +19,22 @@ const ( attrConstNationality = "nationality" ) -//Profile represents the details retrieved for a particular +// Profile represents the details retrieved for a particular user. Consists of +// Yoti attributes: a small piece of information about a Yoti user such as a +// photo of the user or the user's date of birth. type Profile struct { - // AttributeSlice represents a map of the Yoti attributes, each attribute is a small piece of information about a Yoti user such as a photo of the user or the - // user's date of birth. - AttributeSlice []*yotiprotoattr_v3.Attribute + attributeSlice []*yotiprotoattr_v3.Attribute } // Selfie is a photograph of the user. Will be nil if not provided by Yoti func (p Profile) Selfie() *attribute.ImageAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstSelfie { - return attribute.NewImage(a) + attribute, err := attribute.NewImage(a) + + if err == nil { + return attribute + } } } return nil @@ -38,7 +42,7 @@ func (p Profile) Selfie() *attribute.ImageAttribute { // GivenNames represents the user's given names. Will be nil if not provided by Yoti func (p Profile) GivenNames() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstGivenNames { return attribute.NewString(a) } @@ -48,7 +52,7 @@ func (p Profile) GivenNames() *attribute.StringAttribute { // FamilyName represents the user's family name. Will be nil if not provided by Yoti func (p Profile) FamilyName() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstFamilyName { return attribute.NewString(a) } @@ -58,7 +62,7 @@ func (p Profile) FamilyName() *attribute.StringAttribute { //FullName represents the user's full name. Will be nil if not provided by Yoti func (p Profile) FullName() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstFullName { return attribute.NewString(a) } @@ -68,7 +72,7 @@ func (p Profile) FullName() *attribute.StringAttribute { // MobileNumber represents the user's mobile phone number. Will be nil if not provided by Yoti func (p Profile) MobileNumber() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstMobileNumber { return attribute.NewString(a) } @@ -78,7 +82,7 @@ func (p Profile) MobileNumber() *attribute.StringAttribute { // EmailAddress represents the user's email address. Will be nil if not provided by Yoti func (p Profile) EmailAddress() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstEmailAddress { return attribute.NewString(a) } @@ -88,7 +92,7 @@ func (p Profile) EmailAddress() *attribute.StringAttribute { // DateOfBirth represents the user's date of birth. Will be nil if not provided by Yoti. Has an err value which will be filled if there is an error parsing the date. func (p Profile) DateOfBirth() (*attribute.TimeAttribute, error) { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstDateOfBirth { return attribute.NewTime(a) } @@ -98,7 +102,7 @@ func (p Profile) DateOfBirth() (*attribute.TimeAttribute, error) { // Address represents the user's address. Will be nil if not provided by Yoti func (p Profile) Address() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstAddress { return attribute.NewString(a) } @@ -108,7 +112,7 @@ func (p Profile) Address() *attribute.StringAttribute { // StructuredPostalAddress represents the user's address in a JSON format. Will be nil if not provided by Yoti func (p Profile) StructuredPostalAddress() (*attribute.JSONAttribute, error) { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstStructuredPostalAddress { return attribute.NewJSON(a) } @@ -118,7 +122,7 @@ func (p Profile) StructuredPostalAddress() (*attribute.JSONAttribute, error) { // Gender represents the user's gender. Will be nil if not provided by Yoti func (p Profile) Gender() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstGender { return attribute.NewString(a) } @@ -128,7 +132,7 @@ func (p Profile) Gender() *attribute.StringAttribute { // Nationality represents the user's nationality. Will be nil if not provided by Yoti func (p Profile) Nationality() *attribute.StringAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attrConstNationality { return attribute.NewString(a) } @@ -138,7 +142,7 @@ func (p Profile) Nationality() *attribute.StringAttribute { // GetAttribute retrieve an attribute by name on the Yoti profile. Will return nil if attribute is not present. func (p Profile) GetAttribute(attributeName string) *attribute.GenericAttribute { - for _, a := range p.AttributeSlice { + for _, a := range p.attributeSlice { if a.Name == attributeName { return attribute.NewGeneric(a) } From 1e51a212239d77f2fed1d358ce41da3149f8cf50 Mon Sep 17 00:00:00 2001 From: echarrod Date: Mon, 12 Nov 2018 14:04:39 +0000 Subject: [PATCH 5/8] [SDK-443]: Change fields to be unexported: allow read-only access instead --- README.md | 8 ++++---- attribute/genericattribute.go | 29 +++++++++++++++++++++++------ attribute/imageattribute.go | 29 +++++++++++++++++++++++------ attribute/jsonattribute.go | 29 +++++++++++++++++++++++------ attribute/stringattribute.go | 29 +++++++++++++++++++++++------ attribute/timeattribute.go | 29 +++++++++++++++++++++++------ yoti_test.go | 12 ++++++------ 7 files changed, 125 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 6de373ae..a2171b4e 100644 --- a/README.md +++ b/README.md @@ -155,15 +155,15 @@ Similarly, an attribute _verified_ against the data held by an external party wi From each attribute can retrieve the `Anchors`, and subsets `Sources` and `Verifiers` (all as `[]*anchor.Anchor`) as follows: ```Go -givenNamesAnchors := userProfile.GivenNames().Anchors -givenNamesSources := userProfile.GivenNames().Sources -givenNamesVerifiers := userProfile.GivenNames().Verifiers +givenNamesAnchors := userProfile.GivenNames().Anchors() +givenNamesSources := userProfile.GivenNames().Sources() +givenNamesVerifiers := userProfile.GivenNames().Verifiers() ``` You can also retrieve further properties from these respective anchors in the following way: ```Go -givenNamesFirstAnchor := userProfile.GivenNames().Anchors[0] +givenNamesFirstAnchor := userProfile.GivenNames().Anchors()[0] anchorType := givenNamesFirstAnchor.Type signedTimestamp := givenNamesFirstAnchor.SignedTimestamp().Timestamp diff --git a/attribute/genericattribute.go b/attribute/genericattribute.go index 5db7801c..4abe570e 100644 --- a/attribute/genericattribute.go +++ b/attribute/genericattribute.go @@ -12,9 +12,9 @@ import ( type GenericAttribute struct { *yotiprotoattr_v3.Attribute Value interface{} - Anchors []*anchor.Anchor - Sources []*anchor.Anchor - Verifiers []*anchor.Anchor + anchors []*anchor.Anchor + sources []*anchor.Anchor + verifiers []*anchor.Anchor } //NewGeneric creates a new generic attribute @@ -59,8 +59,25 @@ func NewGeneric(a *yotiprotoattr_v3.Attribute) *GenericAttribute { ContentType: a.ContentType, }, Value: value, - Anchors: parsedAnchors, - Sources: anchor.GetSources(parsedAnchors), - Verifiers: anchor.GetVerifiers(parsedAnchors), + anchors: parsedAnchors, + sources: anchor.GetSources(parsedAnchors), + verifiers: anchor.GetVerifiers(parsedAnchors), } } + +// Anchors are the metadata associated with an attribute. They describe +// how an attribute has been provided to Yoti (SOURCE Anchor) and how +// it has been verified (VERIFIER Anchor). +func (a *GenericAttribute) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value was acquired. +func (a *GenericAttribute) Sources() []*anchor.Anchor { + return a.sources +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *GenericAttribute) Verifiers() []*anchor.Anchor { + return a.verifiers +} diff --git a/attribute/imageattribute.go b/attribute/imageattribute.go index 46f81c16..dd321886 100644 --- a/attribute/imageattribute.go +++ b/attribute/imageattribute.go @@ -11,9 +11,9 @@ import ( type ImageAttribute struct { *yotiprotoattr_v3.Attribute Value *Image - Anchors []*anchor.Anchor - Sources []*anchor.Anchor - Verifiers []*anchor.Anchor + anchors []*anchor.Anchor + sources []*anchor.Anchor + verifiers []*anchor.Anchor } //NewImage creates a new Image attribute @@ -42,8 +42,25 @@ func NewImage(a *yotiprotoattr_v3.Attribute) (*ImageAttribute, error) { Data: a.Value, Type: imageType, }, - Anchors: parsedAnchors, - Sources: anchor.GetSources(parsedAnchors), - Verifiers: anchor.GetVerifiers(parsedAnchors), + anchors: parsedAnchors, + sources: anchor.GetSources(parsedAnchors), + verifiers: anchor.GetVerifiers(parsedAnchors), }, nil } + +// Anchors are the metadata associated with an attribute. They describe +// how an attribute has been provided to Yoti (SOURCE Anchor) and how +// it has been verified (VERIFIER Anchor). +func (a *ImageAttribute) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value was acquired. +func (a *ImageAttribute) Sources() []*anchor.Anchor { + return a.sources +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *ImageAttribute) Verifiers() []*anchor.Anchor { + return a.verifiers +} diff --git a/attribute/jsonattribute.go b/attribute/jsonattribute.go index b3ad6fd9..45955483 100644 --- a/attribute/jsonattribute.go +++ b/attribute/jsonattribute.go @@ -12,9 +12,9 @@ import ( type JSONAttribute struct { *yotiprotoattr_v3.Attribute // Value returns the value of a JSON attribute in the form of an interface Value interface{} - Anchors []*anchor.Anchor - Sources []*anchor.Anchor - Verifiers []*anchor.Anchor + anchors []*anchor.Anchor + sources []*anchor.Anchor + verifiers []*anchor.Anchor } //NewJSON creates a new JSON attribute @@ -33,9 +33,9 @@ func NewJSON(a *yotiprotoattr_v3.Attribute) (*JSONAttribute, error) { ContentType: a.ContentType, }, Value: interfaceValue, - Anchors: parsedAnchors, - Sources: anchor.GetSources(parsedAnchors), - Verifiers: anchor.GetVerifiers(parsedAnchors), + anchors: parsedAnchors, + sources: anchor.GetSources(parsedAnchors), + verifiers: anchor.GetVerifiers(parsedAnchors), }, nil } @@ -50,3 +50,20 @@ func UnmarshallJSON(byteValue []byte) (result interface{}, err error) { return unmarshalledJSON, err } + +// Anchors are the metadata associated with an attribute. They describe +// how an attribute has been provided to Yoti (SOURCE Anchor) and how +// it has been verified (VERIFIER Anchor). +func (a *JSONAttribute) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value was acquired. +func (a *JSONAttribute) Sources() []*anchor.Anchor { + return a.sources +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *JSONAttribute) Verifiers() []*anchor.Anchor { + return a.verifiers +} diff --git a/attribute/stringattribute.go b/attribute/stringattribute.go index f86531db..63bda932 100644 --- a/attribute/stringattribute.go +++ b/attribute/stringattribute.go @@ -9,9 +9,9 @@ import ( type StringAttribute struct { *yotiprotoattr_v3.Attribute Value string - Anchors []*anchor.Anchor - Sources []*anchor.Anchor - Verifiers []*anchor.Anchor + anchors []*anchor.Anchor + sources []*anchor.Anchor + verifiers []*anchor.Anchor } //NewString creates a new String attribute @@ -24,8 +24,25 @@ func NewString(a *yotiprotoattr_v3.Attribute) *StringAttribute { ContentType: a.ContentType, }, Value: string(a.Value), - Anchors: parsedAnchors, - Sources: anchor.GetSources(parsedAnchors), - Verifiers: anchor.GetVerifiers(parsedAnchors), + anchors: parsedAnchors, + sources: anchor.GetSources(parsedAnchors), + verifiers: anchor.GetVerifiers(parsedAnchors), } } + +// Anchors are the metadata associated with an attribute. They describe +// how an attribute has been provided to Yoti (SOURCE Anchor) and how +// it has been verified (VERIFIER Anchor). +func (a *StringAttribute) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value was acquired. +func (a *StringAttribute) Sources() []*anchor.Anchor { + return a.sources +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *StringAttribute) Verifiers() []*anchor.Anchor { + return a.verifiers +} diff --git a/attribute/timeattribute.go b/attribute/timeattribute.go index 2bbfa9aa..7884b7fa 100644 --- a/attribute/timeattribute.go +++ b/attribute/timeattribute.go @@ -12,9 +12,9 @@ import ( type TimeAttribute struct { *yotiprotoattr_v3.Attribute Value *time.Time - Anchors []*anchor.Anchor - Sources []*anchor.Anchor - Verifiers []*anchor.Anchor + anchors []*anchor.Anchor + sources []*anchor.Anchor + verifiers []*anchor.Anchor } //NewTime creates a new Time attribute @@ -34,8 +34,25 @@ func NewTime(a *yotiprotoattr_v3.Attribute) (*TimeAttribute, error) { ContentType: a.ContentType, }, Value: &parsedTime, - Anchors: parsedAnchors, - Sources: anchor.GetSources(parsedAnchors), - Verifiers: anchor.GetVerifiers(parsedAnchors), + anchors: parsedAnchors, + sources: anchor.GetSources(parsedAnchors), + verifiers: anchor.GetVerifiers(parsedAnchors), }, nil } + +// Anchors are the metadata associated with an attribute. They describe +// how an attribute has been provided to Yoti (SOURCE Anchor) and how +// it has been verified (VERIFIER Anchor). +func (a *TimeAttribute) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value was acquired. +func (a *TimeAttribute) Sources() []*anchor.Anchor { + return a.sources +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *TimeAttribute) Verifiers() []*anchor.Anchor { + return a.verifiers +} diff --git a/yoti_test.go b/yoti_test.go index 681a9106..09d7dd55 100644 --- a/yoti_test.go +++ b/yoti_test.go @@ -906,9 +906,9 @@ func TestAnchorParser_Passport(t *testing.T) { t.Error(err) } - actualAnchor := actualStructuredPostalAddress.Anchors[0] + actualAnchor := actualStructuredPostalAddress.Anchors()[0] - if actualAnchor != actualStructuredPostalAddress.Sources[0] { + if actualAnchor != actualStructuredPostalAddress.Sources()[0] { t.Error("Anchors and Sources should be the same when there is only one Source") } @@ -949,9 +949,9 @@ func TestAnchorParser_DrivingLicense(t *testing.T) { result := createProfileWithSingleAttribute(attribute) genderAttribute := result.Gender() - resultAnchor := genderAttribute.Anchors[0] + resultAnchor := genderAttribute.Anchors()[0] - if resultAnchor != genderAttribute.Sources[0] { + if resultAnchor != genderAttribute.Sources()[0] { t.Error("Anchors and Sources should be the same when there is only one Source") } @@ -996,9 +996,9 @@ func TestAnchorParser_YotiAdmin(t *testing.T) { t.Error(err) } - resultAnchor := DoB.Anchors[0] + resultAnchor := DoB.Anchors()[0] - if resultAnchor != DoB.Verifiers[0] { + if resultAnchor != DoB.Verifiers()[0] { t.Error("Anchors and Verifiers should be the same when there is only one Verifier") } From 0847b972bd953241e13a45295c6a1cee55859a91 Mon Sep 17 00:00:00 2001 From: echarrod Date: Mon, 12 Nov 2018 14:51:57 +0000 Subject: [PATCH 6/8] [SDK-443]: Restructure example to only process image if selfie is present, add scenarioID --- examples/profile/.env.example | 1 + examples/profile/login.html | 3 ++- examples/profile/main.go | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/examples/profile/.env.example b/examples/profile/.env.example index 44d5459d..68688cef 100644 --- a/examples/profile/.env.example +++ b/examples/profile/.env.example @@ -1,3 +1,4 @@ +YOTI_SCENARIO_ID= YOTI_APPLICATION_ID= YOTI_CLIENT_SDK_ID= YOTI_KEY_FILE_PATH= diff --git a/examples/profile/login.html b/examples/profile/login.html index bd3d4523..ae22c311 100644 --- a/examples/profile/login.html +++ b/examples/profile/login.html @@ -6,7 +6,8 @@ - Use Yoti + Use Yoti diff --git a/examples/profile/main.go b/examples/profile/main.go index c00dc660..d4fe20af 100644 --- a/examples/profile/main.go +++ b/examples/profile/main.go @@ -21,6 +21,7 @@ import ( var ( sdkID string + scenarioID string key []byte client *yoti.Client selfSignedCertName = "yotiSelfSignedCert.pem" @@ -30,6 +31,7 @@ var ( func home(w http.ResponseWriter, req *http.Request) { templateVars := map[string]interface{}{ + "yotiScenarioID": os.Getenv("YOTI_SCENARIO_ID"), "yotiApplicationID": os.Getenv("YOTI_APPLICATION_ID")} t, _ := template.ParseFiles("login.html") @@ -58,11 +60,18 @@ func profile(w http.ResponseWriter, r *http.Request) { userProfile := activityDetails.UserProfile + selfie := userProfile.Selfie() var base64URL string - base64URL, err = userProfile.Selfie().Value.Base64URL() + if selfie != nil { + base64URL, err = selfie.Value.Base64URL() - if err != nil { - log.Fatalf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: %q", err) + if err != nil { + log.Fatalf("Unable to retrieve get Base64 URL of selfie. Error: %q", err) + } + + decodedImage := decodeImage(selfie.Value.Data) + file := createImage() + saveImage(decodedImage, file) } dob, err := userProfile.DateOfBirth() @@ -77,10 +86,6 @@ func profile(w http.ResponseWriter, r *http.Request) { "dateOfBirth": dob, } - decodedImage := decodeImage(userProfile.Selfie().Value.Data) - file := createImage() - saveImage(decodedImage, file) - var t *template.Template t, err = template.ParseFiles("profile.html") if err != nil { From 589428709f8afd0d997c67614d037713a9099a7b Mon Sep 17 00:00:00 2001 From: echarrod Date: Wed, 14 Nov 2018 15:43:09 +0000 Subject: [PATCH 7/8] [SDK-443]: Change Value of attribute to be accessed through a getter function, tidy up tests --- README.md | 20 +++++----- attribute/genericattribute.go | 9 ++++- attribute/imageattribute.go | 9 ++++- attribute/jsonattribute.go | 9 ++++- attribute/stringattribute.go | 9 ++++- attribute/timeattribute.go | 9 ++++- examples/profile/main.go | 4 +- yoti_test.go | 70 ++++++++++++++++------------------- yoticlient.go | 2 +- 9 files changed, 79 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index a2171b4e..9ff9dcf5 100644 --- a/README.md +++ b/README.md @@ -112,15 +112,15 @@ You can then get the user profile from the activityDetails struct: rememberMeID := activityDetails.RememberMeID userProfile := activityDetails.UserProfile -selfie := userProfile.Selfie().Value -givenNames := userProfile.GivenNames().Value -familyName := userProfile.FamilyName().Value -fullName := userProfile.FullName().Value -mobileNumber := userProfile.MobileNumber().Value -emailAddress := userProfile.EmailAddress().Value -address := userProfile.Address().Value -gender := userProfile.Gender().Value -nationality := userProfile.Nationality().Value +selfie := userProfile.Selfie().Value() +givenNames := userProfile.GivenNames().Value() +familyName := userProfile.FamilyName().Value() +fullName := userProfile.FullName().Value() +mobileNumber := userProfile.MobileNumber().Value() +emailAddress := userProfile.EmailAddress().Value() +address := userProfile.Address().Value() +gender := userProfile.Gender().Value() +nationality := userProfile.Nationality().Value() dob, err := userProfile.DateOfBirth() if err != nil { //handle error @@ -132,7 +132,7 @@ if err != nil { If you have chosen Verify Condition on the Yoti Dashboard with the age condition of "Over 18", you can retrieve the user information with the generic .GetAttribute method, which requires the result to be cast to the original type: ```Go -userProfile.GetAttribute("age_over:18").Value.(string) +userProfile.GetAttribute("age_over:18").Value().(string) ``` GetAttribute returns an interface, the value can be acquired through a type assertion. diff --git a/attribute/genericattribute.go b/attribute/genericattribute.go index 4abe570e..245d8641 100644 --- a/attribute/genericattribute.go +++ b/attribute/genericattribute.go @@ -11,7 +11,7 @@ import ( //GenericAttribute is a Yoti attribute which returns a generic value type GenericAttribute struct { *yotiprotoattr_v3.Attribute - Value interface{} + value interface{} anchors []*anchor.Anchor sources []*anchor.Anchor verifiers []*anchor.Anchor @@ -58,13 +58,18 @@ func NewGeneric(a *yotiprotoattr_v3.Attribute) *GenericAttribute { Name: a.Name, ContentType: a.ContentType, }, - Value: value, + value: value, anchors: parsedAnchors, sources: anchor.GetSources(parsedAnchors), verifiers: anchor.GetVerifiers(parsedAnchors), } } +// Value returns the value of the GenericAttribute as an interface +func (a *GenericAttribute) Value() interface{} { + return a.value +} + // Anchors are the metadata associated with an attribute. They describe // how an attribute has been provided to Yoti (SOURCE Anchor) and how // it has been verified (VERIFIER Anchor). diff --git a/attribute/imageattribute.go b/attribute/imageattribute.go index dd321886..359febb7 100644 --- a/attribute/imageattribute.go +++ b/attribute/imageattribute.go @@ -10,7 +10,7 @@ import ( //ImageAttribute is a Yoti attribute which returns an image as its value type ImageAttribute struct { *yotiprotoattr_v3.Attribute - Value *Image + value *Image anchors []*anchor.Anchor sources []*anchor.Anchor verifiers []*anchor.Anchor @@ -38,7 +38,7 @@ func NewImage(a *yotiprotoattr_v3.Attribute) (*ImageAttribute, error) { Name: a.Name, ContentType: a.ContentType, }, - Value: &Image{ + value: &Image{ Data: a.Value, Type: imageType, }, @@ -48,6 +48,11 @@ func NewImage(a *yotiprotoattr_v3.Attribute) (*ImageAttribute, error) { }, nil } +// Value returns the value of the ImageAttribute as *Image +func (a *ImageAttribute) Value() *Image { + return a.value +} + // Anchors are the metadata associated with an attribute. They describe // how an attribute has been provided to Yoti (SOURCE Anchor) and how // it has been verified (VERIFIER Anchor). diff --git a/attribute/jsonattribute.go b/attribute/jsonattribute.go index 45955483..ef729289 100644 --- a/attribute/jsonattribute.go +++ b/attribute/jsonattribute.go @@ -11,7 +11,7 @@ import ( //JSONAttribute is a Yoti attribute which returns an interface as its value type JSONAttribute struct { *yotiprotoattr_v3.Attribute // Value returns the value of a JSON attribute in the form of an interface - Value interface{} + value interface{} anchors []*anchor.Anchor sources []*anchor.Anchor verifiers []*anchor.Anchor @@ -32,7 +32,7 @@ func NewJSON(a *yotiprotoattr_v3.Attribute) (*JSONAttribute, error) { Name: a.Name, ContentType: a.ContentType, }, - Value: interfaceValue, + value: interfaceValue, anchors: parsedAnchors, sources: anchor.GetSources(parsedAnchors), verifiers: anchor.GetVerifiers(parsedAnchors), @@ -51,6 +51,11 @@ func UnmarshallJSON(byteValue []byte) (result interface{}, err error) { return unmarshalledJSON, err } +// Value returns the value of the JSONAttribute as an interface +func (a *JSONAttribute) Value() interface{} { + return a.value +} + // Anchors are the metadata associated with an attribute. They describe // how an attribute has been provided to Yoti (SOURCE Anchor) and how // it has been verified (VERIFIER Anchor). diff --git a/attribute/stringattribute.go b/attribute/stringattribute.go index 63bda932..abbfbdc7 100644 --- a/attribute/stringattribute.go +++ b/attribute/stringattribute.go @@ -8,7 +8,7 @@ import ( //StringAttribute is a Yoti attribute which returns a string as its value type StringAttribute struct { *yotiprotoattr_v3.Attribute - Value string + value string anchors []*anchor.Anchor sources []*anchor.Anchor verifiers []*anchor.Anchor @@ -23,13 +23,18 @@ func NewString(a *yotiprotoattr_v3.Attribute) *StringAttribute { Name: a.Name, ContentType: a.ContentType, }, - Value: string(a.Value), + value: string(a.Value), anchors: parsedAnchors, sources: anchor.GetSources(parsedAnchors), verifiers: anchor.GetVerifiers(parsedAnchors), } } +// Value returns the value of the StringAttribute as a string +func (a *StringAttribute) Value() string { + return a.value +} + // Anchors are the metadata associated with an attribute. They describe // how an attribute has been provided to Yoti (SOURCE Anchor) and how // it has been verified (VERIFIER Anchor). diff --git a/attribute/timeattribute.go b/attribute/timeattribute.go index 7884b7fa..5487fe73 100644 --- a/attribute/timeattribute.go +++ b/attribute/timeattribute.go @@ -11,7 +11,7 @@ import ( //TimeAttribute is a Yoti attribute which returns a time as its value type TimeAttribute struct { *yotiprotoattr_v3.Attribute - Value *time.Time + value *time.Time anchors []*anchor.Anchor sources []*anchor.Anchor verifiers []*anchor.Anchor @@ -33,13 +33,18 @@ func NewTime(a *yotiprotoattr_v3.Attribute) (*TimeAttribute, error) { Name: a.Name, ContentType: a.ContentType, }, - Value: &parsedTime, + value: &parsedTime, anchors: parsedAnchors, sources: anchor.GetSources(parsedAnchors), verifiers: anchor.GetVerifiers(parsedAnchors), }, nil } +// Value returns the value of the TimeAttribute as *time.Time +func (a *TimeAttribute) Value() *time.Time { + return a.value +} + // Anchors are the metadata associated with an attribute. They describe // how an attribute has been provided to Yoti (SOURCE Anchor) and how // it has been verified (VERIFIER Anchor). diff --git a/examples/profile/main.go b/examples/profile/main.go index d4fe20af..671bd7c4 100644 --- a/examples/profile/main.go +++ b/examples/profile/main.go @@ -63,13 +63,13 @@ func profile(w http.ResponseWriter, r *http.Request) { selfie := userProfile.Selfie() var base64URL string if selfie != nil { - base64URL, err = selfie.Value.Base64URL() + base64URL, err = selfie.Value().Base64URL() if err != nil { log.Fatalf("Unable to retrieve get Base64 URL of selfie. Error: %q", err) } - decodedImage := decodeImage(selfie.Value.Data) + decodedImage := decodeImage(selfie.Value().Data) file := createImage() saveImage(decodedImage, file) } diff --git a/yoti_test.go b/yoti_test.go index 09d7dd55..2d079501 100644 --- a/yoti_test.go +++ b/yoti_test.go @@ -21,9 +21,9 @@ import ( const token = "NpdmVVGC-28356678-c236-4518-9de4-7a93009ccaf0-c5f92f2a-5539-453e-babc-9b06e1d6b7de" const encryptedToken = "b6H19bUCJhwh6WqQX_sEHWX9RP-A_ANr1fkApwA4Dp2nJQFAjrF9e6YCXhNBpAIhfHnN0iXubyXxXZMNwNMSQ5VOxkqiytrvPykfKQWHC6ypSbfy0ex8ihndaAXG5FUF-qcU8QaFPMy6iF3x0cxnY0Ij0kZj0Ng2t6oiNafb7AhT-VGXxbFbtZu1QF744PpWMuH0LVyBsAa5N5GJw2AyBrnOh67fWMFDKTJRziP5qCW2k4h5vJfiYr_EOiWKCB1d_zINmUm94ZffGXxcDAkq-KxhN1ZuNhGlJ2fKcFh7KxV0BqlUWPsIEiwS0r9CJ2o1VLbEs2U_hCEXaqseEV7L29EnNIinEPVbL4WR7vkF6zQCbK_cehlk2Qwda-VIATqupRO5grKZN78R9lBitvgilDaoE7JB_VFcPoljGQ48kX0wje1mviX4oJHhuO8GdFITS5LTbojGVQWT7LUNgAUe0W0j-FLHYYck3v84OhWTqads5_jmnnLkp9bdJSRuJF0e8pNdePnn2lgF-GIcyW_0kyGVqeXZrIoxnObLpF-YeUteRBKTkSGFcy7a_V_DLiJMPmH8UXDLOyv8TVt3ppzqpyUrLN2JVMbL5wZ4oriL2INEQKvw_boDJjZDGeRlu5m1y7vGDNBRDo64-uQM9fRUULPw-YkABNwC0DeShswzT00=" +const sdkID = "fake-sdk-id" func TestYotiClient_KeyLoad_Failure(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key-invalid-format.pem") var requester = func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { @@ -43,7 +43,6 @@ func TestYotiClient_KeyLoad_Failure(t *testing.T) { } func TestYotiClient_HttpFailure_ReturnsFailure(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") var requester = func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { @@ -62,7 +61,6 @@ func TestYotiClient_HttpFailure_ReturnsFailure(t *testing.T) { } func TestYotiClient_HttpFailure_ReturnsProfileNotFound(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") var requester = func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { @@ -81,7 +79,6 @@ func TestYotiClient_HttpFailure_ReturnsProfileNotFound(t *testing.T) { } func TestYotiClient_SharingFailure_ReturnsFailure(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") var requester = func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { @@ -101,7 +98,6 @@ func TestYotiClient_SharingFailure_ReturnsFailure(t *testing.T) { } func TestYotiClient_TokenDecodedSuccessfully(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") expectedAbsoluteURL := "/api/v1/profile/" + token @@ -132,7 +128,6 @@ func TestYotiClient_TokenDecodedSuccessfully(t *testing.T) { } func TestYotiClient_ParseProfile_Success(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") wrappedReceiptKey := "kyHPjq2+Y48cx+9yS/XzmW09jVUylSdhbP+3Q9Tc9p6bCEnyfa8vj38AIu744RzzE+Dc4qkSF21VfzQKtJVILfOXu5xRc7MYa5k3zWhjiesg/gsrv7J4wDyyBpHIJB8TWXnubYMbSYQJjlsfwyxE9kGe0YI08pRo2Tiht0bfR5Z/YrhAk4UBvjp84D+oyug/1mtGhKphA4vgPhQ9/y2wcInYxju7Q6yzOsXGaRUXR38Tn2YmY9OBgjxiTnhoYJFP1X9YJkHeWMW0vxF1RHxgIVrpf7oRzdY1nq28qzRg5+wC7cjRpS2i/CKUAo0oVG4pbpXsaFhaTewStVC7UFtA77JHb3EnF4HcSWMnK5FM7GGkL9MMXQenh11NZHKPWXpux0nLZ6/vwffXZfsiyTIcFL/NajGN8C/hnNBljoQ+B3fzWbjcq5ueUOPwARZ1y38W83UwMynzkud/iEdHLaZIu4qUCRkfSxJg7Dc+O9/BdiffkOn2GyFmNjVeq754DCUypxzMkjYxokedN84nK13OU4afVyC7t5DDxAK/MqAc69NCBRLqMi5f8BMeOZfMcSWPGC9a2Qu8VgG125TuZT4+wIykUhGyj3Bb2/fdPsxwuKFR+E0uqs0ZKvcv1tkNRRtKYBqTacgGK9Yoehg12cyLrITLdjU1fmIDn4/vrhztN5w=" @@ -183,12 +178,12 @@ func TestYotiClient_ParseProfile_Success(t *testing.T) { expectedSelfieValue := "selfie0123456789" if profile.Selfie() == nil { t.Error(`expected selfie attribute, but it was not present in the returned profile`) - } else if !cmp.Equal(profile.Selfie().Value.Data, []byte(expectedSelfieValue)) { - t.Errorf("expected selfie %q, instead received %q", expectedSelfieValue, string(profile.Selfie().Value.Data)) + } else if !cmp.Equal(profile.Selfie().Value().Data, []byte(expectedSelfieValue)) { + t.Errorf("expected selfie %q, instead received %q", expectedSelfieValue, string(profile.Selfie().Value().Data)) } - if !cmp.Equal(profile.MobileNumber().Value, "phone_number0123456789") { - t.Errorf("expected mobileNumber %q, instead received %q", "phone_number0123456789", profile.MobileNumber().Value) + if !cmp.Equal(profile.MobileNumber().Value(), "phone_number0123456789") { + t.Errorf("expected mobileNumber %q, instead received %q", "phone_number0123456789", profile.MobileNumber().Value()) } expectedDoB := time.Date(1980, time.January, 1, 0, 0, 0, 0, time.UTC) @@ -200,13 +195,12 @@ func TestYotiClient_ParseProfile_Success(t *testing.T) { if actualDoB == nil { t.Error(`expected date of birth, but it was not present in the returned profile`) - } else if !actualDoB.Value.Equal(expectedDoB) { - t.Errorf("expected date of birth: %q, instead received: %q", expectedDoB.Format(time.UnixDate), actualDoB.Value.Format(time.UnixDate)) + } else if !actualDoB.Value().Equal(expectedDoB) { + t.Errorf("expected date of birth: %q, instead received: %q", expectedDoB.Format(time.UnixDate), actualDoB.Value().Format(time.UnixDate)) } } func TestYotiClient_ParseWithoutProfile_Success(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") wrappedReceiptKey := "kyHPjq2+Y48cx+9yS/XzmW09jVUylSdhbP+3Q9Tc9p6bCEnyfa8vj38AIu744RzzE+Dc4qkSF21VfzQKtJVILfOXu5xRc7MYa5k3zWhjiesg/gsrv7J4wDyyBpHIJB8TWXnubYMbSYQJjlsfwyxE9kGe0YI08pRo2Tiht0bfR5Z/YrhAk4UBvjp84D+oyug/1mtGhKphA4vgPhQ9/y2wcInYxju7Q6yzOsXGaRUXR38Tn2YmY9OBgjxiTnhoYJFP1X9YJkHeWMW0vxF1RHxgIVrpf7oRzdY1nq28qzRg5+wC7cjRpS2i/CKUAo0oVG4pbpXsaFhaTewStVC7UFtA77JHb3EnF4HcSWMnK5FM7GGkL9MMXQenh11NZHKPWXpux0nLZ6/vwffXZfsiyTIcFL/NajGN8C/hnNBljoQ+B3fzWbjcq5ueUOPwARZ1y38W83UwMynzkud/iEdHLaZIu4qUCRkfSxJg7Dc+O9/BdiffkOn2GyFmNjVeq754DCUypxzMkjYxokedN84nK13OU4afVyC7t5DDxAK/MqAc69NCBRLqMi5f8BMeOZfMcSWPGC9a2Qu8VgG125TuZT4+wIykUhGyj3Bb2/fdPsxwuKFR+E0uqs0ZKvcv1tkNRRtKYBqTacgGK9Yoehg12cyLrITLdjU1fmIDn4/vrhztN5w=" @@ -270,7 +264,6 @@ func TestYotiClient_SupportedHttpMethod(t *testing.T) { } func TestYotiClient_PerformAmlCheck_Success(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") var requester = func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { @@ -304,7 +297,6 @@ func TestYotiClient_PerformAmlCheck_Success(t *testing.T) { } func TestYotiClient_PerformAmlCheck_Unsuccessful(t *testing.T) { - sdkID := "fake-sdk-id" key, _ := ioutil.ReadFile("test-key.pem") var requester = func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { @@ -560,8 +552,8 @@ func TestProfile_GetAttribute_String(t *testing.T) { t.Errorf("Retrieved attribute does not have the correct name. Expected %q, actual: %q", attributeName, att.Name) } - if !cmp.Equal(att.Value.(string), attributeValueString) { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value) + if !cmp.Equal(att.Value().(string), attributeValueString) { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value()) } } @@ -583,8 +575,8 @@ func TestProfile_GetAttribute_Time(t *testing.T) { result := createProfileWithSingleAttribute(attr) att := result.GetAttribute(attributeName) - if !cmp.Equal(expectedDate, att.Value.(*time.Time).UTC()) { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", expectedDate, att.Value.(*time.Time)) + if !cmp.Equal(expectedDate, att.Value().(*time.Time).UTC()) { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", expectedDate, att.Value().(*time.Time)) } } @@ -602,8 +594,8 @@ func TestProfile_GetAttribute_Jpeg(t *testing.T) { result := createProfileWithSingleAttribute(attr) att := result.GetAttribute(attributeName) - if !cmp.Equal(att.Value.([]byte), attributeValue) { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value) + if !cmp.Equal(att.Value().([]byte), attributeValue) { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value()) } } @@ -621,8 +613,8 @@ func TestProfile_GetAttribute_Png(t *testing.T) { result := createProfileWithSingleAttribute(attr) att := result.GetAttribute(attributeName) - if !cmp.Equal(att.Value.([]byte), attributeValue) { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value) + if !cmp.Equal(att.Value().([]byte), attributeValue) { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value()) } } @@ -641,7 +633,7 @@ func TestProfile_GetAttribute_Bool(t *testing.T) { result := createProfileWithSingleAttribute(attr) att := result.GetAttribute(attributeName) - boolValue, err := strconv.ParseBool(att.Value.(string)) + boolValue, err := strconv.ParseBool(att.Value().(string)) if err != nil { t.Errorf("Unable to parse string to bool. Error: %s", err) } @@ -672,7 +664,7 @@ func TestProfile_GetAttribute_JSON(t *testing.T) { result := createProfileWithSingleAttribute(attr) att := result.GetAttribute(attributeName) - retrievedAttributeInterfaceArray := att.Value.([]interface{}) + retrievedAttributeInterfaceArray := att.Value().([]interface{}) parsedMap := retrievedAttributeInterfaceArray[0].(map[string]interface{}) actualAddressFormat := parsedMap["address_format"] @@ -700,8 +692,8 @@ func TestProfile_GetAttribute_Undefined(t *testing.T) { t.Errorf("Retrieved attribute does not have the correct name. Expected %q, actual: %q", attributeName, att.Name) } - if !cmp.Equal(att.Value.(string), attributeValueString) { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value) + if !cmp.Equal(att.Value().(string), attributeValueString) { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, att.Value()) } } func TestProfile_GetAttribute_ReturnsNil(t *testing.T) { @@ -730,8 +722,8 @@ func TestProfile_StringAttribute(t *testing.T) { result := createProfileWithSingleAttribute(as) - if result.Nationality().Value != attributeValueString { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValueString, result.Nationality().Value) + if result.Nationality().Value() != attributeValueString { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValueString, result.Nationality().Value()) } if !cmp.Equal(result.Nationality().ContentType, yotiprotoattr_v3.ContentType_STRING) { @@ -757,8 +749,8 @@ func TestProfile_AttributeProperty_RetrievesAttribute(t *testing.T) { t.Errorf("Retrieved attribute does not have the correct name. Expected %q, actual: %q", attributeName, selfie.Name) } - if !reflect.DeepEqual(attributeValue, selfie.Value.Data) { - t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, selfie.Value.Data) + if !reflect.DeepEqual(attributeValue, selfie.Value().Data) { + t.Errorf("Retrieved attribute does not have the correct value. Expected %q, actual: %q", attributeValue, selfie.Value().Data) } if !cmp.Equal(selfie.ContentType, yotiprotoattr_v3.ContentType_PNG) { @@ -780,8 +772,8 @@ func TestAttributeImage_Image_Png(t *testing.T) { result := createProfileWithSingleAttribute(attributeImage) selfie := result.Selfie() - if !cmp.Equal(selfie.Value.Data, byteValue) { - t.Errorf("Retrieved attribute does not have the correct Image. Expected %v, actual: %v", byteValue, selfie.Value.Data) + if !cmp.Equal(selfie.Value().Data, byteValue) { + t.Errorf("Retrieved attribute does not have the correct Image. Expected %v, actual: %v", byteValue, selfie.Value().Data) } } @@ -799,8 +791,8 @@ func TestAttributeImage_Image_Jpeg(t *testing.T) { result := createProfileWithSingleAttribute(attributeImage) selfie := result.Selfie() - if !cmp.Equal(selfie.Value.Data, byteValue) { - t.Errorf("Retrieved attribute does not have the correct byte value. Expected %v, actual: %v", byteValue, selfie.Value.Data) + if !cmp.Equal(selfie.Value().Data, byteValue) { + t.Errorf("Retrieved attribute does not have the correct byte value. Expected %v, actual: %v", byteValue, selfie.Value().Data) } } @@ -817,8 +809,8 @@ func TestAttributeImage_Image_Default(t *testing.T) { result := createProfileWithSingleAttribute(attributeImage) selfie := result.Selfie() - if !cmp.Equal(selfie.Value.Data, byteValue) { - t.Errorf("Retrieved attribute does not have the correct byte value. Expected %v, actual: %v", byteValue, selfie.Value.Data) + if !cmp.Equal(selfie.Value().Data, byteValue) { + t.Errorf("Retrieved attribute does not have the correct byte value. Expected %v, actual: %v", byteValue, selfie.Value().Data) } } func TestAttributeImage_Base64Selfie_Png(t *testing.T) { @@ -838,7 +830,7 @@ func TestAttributeImage_Base64Selfie_Png(t *testing.T) { expectedBase64Selfie := "data:image/png;base64;," + base64ImageExpectedValue - base64Selfie, err := result.Selfie().Value.Base64URL() + base64Selfie, err := result.Selfie().Value().Base64URL() if err != nil { t.Error(err) @@ -866,7 +858,7 @@ func TestAttributeImage_Base64URL_Jpeg(t *testing.T) { expectedBase64Selfie := "data:image/jpeg;base64;," + base64ImageExpectedValue - base64Selfie, err := result.Selfie().Value.Base64URL() + base64Selfie, err := result.Selfie().Value().Base64URL() if err != nil { t.Error(err) diff --git a/yoticlient.go b/yoticlient.go index b07fb060..a4ce2af4 100644 --- a/yoticlient.go +++ b/yoticlient.go @@ -318,7 +318,7 @@ func ensureAddressProfile(profile Profile) (address string, err error) { if structuredPostalAddress, err = profile.StructuredPostalAddress(); err == nil { if (structuredPostalAddress != nil && !reflect.DeepEqual(structuredPostalAddress, attribute.JSONAttribute{})) { var formattedAddress string - formattedAddress, err = retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress.Value) + formattedAddress, err = retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress.Value()) if err == nil { return formattedAddress, nil } From a2f671e48ad9ac55bad8b4514a63fee3113054e2 Mon Sep 17 00:00:00 2001 From: echarrod Date: Thu, 15 Nov 2018 10:15:49 +0000 Subject: [PATCH 8/8] [SDK-344]: Specify type of variables explicitly in README --- README.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9ff9dcf5..df8a6212 100644 --- a/README.md +++ b/README.md @@ -109,23 +109,24 @@ if len(errStrings) != 0 { You can then get the user profile from the activityDetails struct: ```Go -rememberMeID := activityDetails.RememberMeID -userProfile := activityDetails.UserProfile - -selfie := userProfile.Selfie().Value() -givenNames := userProfile.GivenNames().Value() -familyName := userProfile.FamilyName().Value() -fullName := userProfile.FullName().Value() -mobileNumber := userProfile.MobileNumber().Value() -emailAddress := userProfile.EmailAddress().Value() -address := userProfile.Address().Value() -gender := userProfile.Gender().Value() -nationality := userProfile.Nationality().Value() -dob, err := userProfile.DateOfBirth() +var rememberMeID string = activityDetails.RememberMeID +var userProfile yoti.Profile = activityDetails.UserProfile + +var selfie = userProfile.Selfie().Value() +var givenNames string = userProfile.GivenNames().Value() +var familyName string = userProfile.FamilyName().Value() +var fullName string = userProfile.FullName().Value() +var mobileNumber string = userProfile.MobileNumber().Value() +var emailAddress string = userProfile.EmailAddress().Value() +var address string = userProfile.Address().Value() +var gender string = userProfile.Gender().Value() +var nationality string = userProfile.Nationality().Value() +var dateOfBirth *time.Time +dobAttr, err := userProfile.DateOfBirth() if err != nil { - //handle error + //handle error } else { - dateofBirth = dob + dateOfBirth = dobAttr.Value() } ``` @@ -163,12 +164,12 @@ givenNamesVerifiers := userProfile.GivenNames().Verifiers() You can also retrieve further properties from these respective anchors in the following way: ```Go -givenNamesFirstAnchor := userProfile.GivenNames().Anchors()[0] +var givenNamesFirstAnchor *anchor.Anchor = userProfile.GivenNames().Anchors()[0] -anchorType := givenNamesFirstAnchor.Type -signedTimestamp := givenNamesFirstAnchor.SignedTimestamp().Timestamp -subType := givenNamesFirstAnchor.SubType() -value := givenNamesFirstAnchor.Value() +var anchorType anchor.Type = givenNamesFirstAnchor.Type +var signedTimestamp *time.Time = givenNamesFirstAnchor.SignedTimestamp().Timestamp +var subType string = givenNamesFirstAnchor.SubType() +var value []string = givenNamesFirstAnchor.Value() ``` ## Handling Users
{{$key}} {{$value}}