From 86369af9238c15b5169a4aba31e354d764582b05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:39:02 +0000 Subject: [PATCH 01/18] Initial plan From efd3b073b7707803c3da208549a7258b5b7f9060 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:45:58 +0000 Subject: [PATCH 02/18] Update all examples to use new Yoti AI Services API endpoints Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- README.md | 39 ++++++++++++++++++- dotnet/CoreExample/CoreExample/.env | 3 +- dotnet/CoreExample/CoreExample/Program.cs | 4 +- dotnet/CoreExample/README.md | 3 +- go/.env.example | 3 +- go/README.md | 3 +- go/main.go | 5 ++- java/README.md | 3 +- .../java/com/yoti/agescan/Application.java | 4 +- .../src/main/resources/application.properties | 3 +- javascript/.env | 3 +- javascript/README.md | 3 +- javascript/index.js | 4 +- php/README.md | 8 ++++ php/index.php | 4 +- python/.env | 3 +- python/README.md | 3 +- python/example.py | 4 +- 18 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 php/README.md diff --git a/README.md b/README.md index 9a5de7b..2607d8c 100644 --- a/README.md +++ b/README.md @@ -1 +1,38 @@ -# age-scan-examples \ No newline at end of file +# age-scan-examples + +This repository contains examples for integrating with Yoti AI Services API in multiple programming languages. + +## API Overview + +The examples use the Yoti AI Services API with the following structure: + +- **Base URL**: `https://api.yoti.com/ai/v1` +- **Available Endpoints**: + - `/age` - Age estimation only + - `/antispoofing` - Liveness/antispoofing check only + - `/age-antispoofing` - Combined age estimation and antispoofing check (default) + +## Available Examples + +- [Python](./python/README.md) +- [JavaScript/Node.js](./javascript/README.md) +- [Java](./java/README.md) +- [Go](./go/README.md) +- [.NET Core](./dotnet/CoreExample/README.md) +- [PHP](./php/README.md) + +## Getting Started + +Each example requires: + +1. A Yoti SDK ID and PEM file (obtain from [Yoti Hub](https://hub.yoti.com)) +2. Configuration of environment variables or properties file +3. An image file for testing (provided as `testimage.jpg` or `image.jpeg`) + +See individual example READMEs for language-specific setup instructions. + +## Reference + +For more information, see: +- [Yoti AI Services API Documentation](https://developers.yoti.com/ai-services-api) +- [Web FCM Demo](https://github.com/getyoti/web-fcm-demo) \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/.env b/dotnet/CoreExample/CoreExample/.env index 39d23ce..5ec82e5 100644 --- a/dotnet/CoreExample/CoreExample/.env +++ b/dotnet/CoreExample/CoreExample/.env @@ -1,5 +1,6 @@ TEST_IMAGE_PATH = "testimage.jpg" -BASE_URL = "https://api.yoti.com" +BASE_URL = "https://api.yoti.com/ai/v1" +ENDPOINT = "age-antispoofing" PEM_FILE_PATH = "keys/key.pem" SDK_ID = "" diff --git a/dotnet/CoreExample/CoreExample/Program.cs b/dotnet/CoreExample/CoreExample/Program.cs index 10921c0..a87b025 100644 --- a/dotnet/CoreExample/CoreExample/Program.cs +++ b/dotnet/CoreExample/CoreExample/Program.cs @@ -52,8 +52,8 @@ static void Main(string[] args) byte[] byteContent = Encoding.UTF8.GetBytes(serializedRequest); Request request = new RequestBuilder() - .WithBaseUri(new Uri(DotNetEnv.Env.GetString("BASE_URL") + "/api/v1/age-verification")) - .WithEndpoint("/checks") + .WithBaseUri(new Uri(DotNetEnv.Env.GetString("BASE_URL"))) + .WithEndpoint("/" + DotNetEnv.Env.GetString("ENDPOINT")) .WithHttpMethod(HttpMethod.Post) .WithKeyPair(key) .WithHeader("X-Yoti-Auth-Id", DotNetEnv.Env.GetString("SDK_ID")) diff --git a/dotnet/CoreExample/README.md b/dotnet/CoreExample/README.md index bbfcc8c..37052c3 100644 --- a/dotnet/CoreExample/README.md +++ b/dotnet/CoreExample/README.md @@ -1,6 +1,7 @@ # .NET Core Example - Save your PEM file into the keys directory and name it key.pem -- Edit the `.env` file, adding client SDK ID and BASE_URL +- Edit the `.env` file, adding client SDK ID +- Optional - Update the ENDPOINT in `.env` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) - Optional - Replace the `testimage.jpg` with your own. - Run the project with `dotnet run -p CoreExample.csproj` diff --git a/go/.env.example b/go/.env.example index 39d23ce..5ec82e5 100644 --- a/go/.env.example +++ b/go/.env.example @@ -1,5 +1,6 @@ TEST_IMAGE_PATH = "testimage.jpg" -BASE_URL = "https://api.yoti.com" +BASE_URL = "https://api.yoti.com/ai/v1" +ENDPOINT = "age-antispoofing" PEM_FILE_PATH = "keys/key.pem" SDK_ID = "" diff --git a/go/README.md b/go/README.md index 43115e2..a2f15ba 100644 --- a/go/README.md +++ b/go/README.md @@ -1,6 +1,7 @@ # Golang Example - Save your PEM file into the keys directory and name it key.pem -- Edit the `.env` file, adding client SDK ID and BASE_URL +- Copy `.env.example` to `.env` and add your client SDK ID +- Optional - Update the ENDPOINT in `.env` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) - Optional - Replace the `testimage.jpg` with your own. - Run the project with `go run main.go` diff --git a/go/main.go b/go/main.go index 8577e17..d65d176 100644 --- a/go/main.go +++ b/go/main.go @@ -22,6 +22,7 @@ func main(){ sdkID := os.Getenv("SDK_ID") baseURL := os.Getenv("BASE_URL") + endpoint := os.Getenv("ENDPOINT") keyFile := os.Getenv("PEM_FILE_PATH") imgPath := os.Getenv("TEST_IMAGE_PATH") @@ -43,8 +44,8 @@ func main(){ // Create request req,_ := requests.SignedRequest{ HTTPMethod: http.MethodPost, - BaseURL: baseURL + "/api/v1/age-verification", - Endpoint: "/checks", + BaseURL: baseURL, + Endpoint: "/" + endpoint, Headers: map[string][]string{ "Content-Type": {"application/json"}, "Accept": {"application/json"}, diff --git a/java/README.md b/java/README.md index f0bcff4..797bec3 100644 --- a/java/README.md +++ b/java/README.md @@ -1,7 +1,8 @@ # Java Example - Save your PEM file into the keys directory and name it key.pem -- Edit the `application.properties` file, adding client SDK ID and BASE_URL +- Edit the `application.properties` file, adding client SDK ID +- Optional - Update the ENDPOINT in `application.properties` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) - Optional - Replace the testimage.jpg with your own. - Build the project `mvn clean package` - Run the example `java -jar target/age-scan-1.0.jar` diff --git a/java/src/main/java/com/yoti/agescan/Application.java b/java/src/main/java/com/yoti/agescan/Application.java index 4542c02..3a46ec0 100644 --- a/java/src/main/java/com/yoti/agescan/Application.java +++ b/java/src/main/java/com/yoti/agescan/Application.java @@ -54,8 +54,8 @@ public static void main(String[] args) { try { SignedRequest signedRequest = SignedRequestBuilder.newInstance() .withKeyPair(findKeyPair()) - .withBaseUrl(prop.getProperty("HOST") + "/api/v1/age-verification") - .withEndpoint("/checks") + .withBaseUrl(prop.getProperty("BASE_URL")) + .withEndpoint("/" + prop.getProperty("ENDPOINT")) .withPayload(payload) .withHttpMethod("POST") .withHeader("X-Yoti-Auth-Id", prop.getProperty("SDK_ID")) diff --git a/java/src/main/resources/application.properties b/java/src/main/resources/application.properties index d7e7039..f5811fe 100644 --- a/java/src/main/resources/application.properties +++ b/java/src/main/resources/application.properties @@ -1,4 +1,5 @@ TEST_IMAGE_PATH = testimage.jpg -HOST = +BASE_URL = https://api.yoti.com/ai/v1 +ENDPOINT = age-antispoofing PEM_FILE_PATH = keys/key.pem SDK_ID = diff --git a/javascript/.env b/javascript/.env index d5b8bdc..5ec82e5 100644 --- a/javascript/.env +++ b/javascript/.env @@ -1,5 +1,6 @@ TEST_IMAGE_PATH = "testimage.jpg" -BASE_URL = "" +BASE_URL = "https://api.yoti.com/ai/v1" +ENDPOINT = "age-antispoofing" PEM_FILE_PATH = "keys/key.pem" SDK_ID = "" diff --git a/javascript/README.md b/javascript/README.md index adda743..91efdaa 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -2,6 +2,7 @@ - Run `npm install` - Save your PEM file into the keys directory and name it key.pem -- Edit the `.env` file, adding client SDK ID and BASE_URL +- Edit the `.env` file, adding client SDK ID +- Optional - Update the ENDPOINT in `.env` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) - Optional - Replace the `testimage.jpg` with your own. - Run the project with `npm start` diff --git a/javascript/index.js b/javascript/index.js index 27ae729..14ff857 100644 --- a/javascript/index.js +++ b/javascript/index.js @@ -7,9 +7,9 @@ var image = fs.readFileSync(process.env.TEST_IMAGE_PATH); var imageRequest = {"data": image.toString('base64')} const request = new RequestBuilder() - .withBaseUrl(process.env.BASE_URL + '/api/v1/age-verification') + .withBaseUrl(process.env.BASE_URL) .withPemFilePath(process.env.PEM_FILE_PATH) - .withEndpoint('/checks') + .withEndpoint('/' + process.env.ENDPOINT) .withPayload(new Payload(imageRequest)) .withMethod('POST') .withHeader('X-Yoti-Auth-Id', process.env.SDK_ID) diff --git a/php/README.md b/php/README.md new file mode 100644 index 0000000..5b9e7b8 --- /dev/null +++ b/php/README.md @@ -0,0 +1,8 @@ +# PHP Example + +- Run `composer install` +- Save your PEM file into the same directory and name it key.pem +- Edit the `index.php` file, replacing `` with your client SDK ID +- Optional - Update the endpoint in `index.php` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) +- Optional - Replace the `image.jpeg` with your own. +- Run the project with `php index.php` diff --git a/php/index.php b/php/index.php index 768a645..75de1eb 100644 --- a/php/index.php +++ b/php/index.php @@ -10,9 +10,9 @@ $payload = [ "data" => base64_encode($image) ]; $request = (new RequestBuilder()) - ->withBaseUrl('/api/v1/age-verification') + ->withBaseUrl('https://api.yoti.com/ai/v1') ->withPemFilePath('key.pem') - ->withEndpoint('/checks') + ->withEndpoint('/age-antispoofing') ->withMethod('POST') ->withPayload(Payload::fromJsonData($payload)) ->withHeader('X-Yoti-Auth-Id', '') diff --git a/python/.env b/python/.env index d5b8bdc..5ec82e5 100644 --- a/python/.env +++ b/python/.env @@ -1,5 +1,6 @@ TEST_IMAGE_PATH = "testimage.jpg" -BASE_URL = "" +BASE_URL = "https://api.yoti.com/ai/v1" +ENDPOINT = "age-antispoofing" PEM_FILE_PATH = "keys/key.pem" SDK_ID = "" diff --git a/python/README.md b/python/README.md index 4cf2ce4..0a2aa1c 100644 --- a/python/README.md +++ b/python/README.md @@ -1,6 +1,7 @@ # Python Example - Save your PEM file into the keys directory and name it key.pem -- Edit the `.env` file, adding client SDK ID and BASE_URL +- Edit the `.env` file, adding client SDK ID +- Optional - Update the ENDPOINT in `.env` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) - Optional - Replace the testimage.jpg with your own. - Run the example `python example.py` diff --git a/python/example.py b/python/example.py index ccf079b..42eabe5 100644 --- a/python/example.py +++ b/python/example.py @@ -23,8 +23,8 @@ def generate_session(): SignedRequest .builder() .with_pem_file(os.getenv('PEM_FILE_PATH')) - .with_base_url(os.getenv('HOST') + "/api/v1/age-verification") - .with_endpoint("/checks") + .with_base_url(os.getenv('BASE_URL')) + .with_endpoint("/" + os.getenv('ENDPOINT')) .with_http_method("POST") .with_header("X-Yoti-Auth-Id", os.getenv('SDK_ID')) .with_payload(payload_string) From 20073ae71a8bdf9123dcb2329d029bae29cc8e4e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:47:19 +0000 Subject: [PATCH 03/18] Make PHP example use environment variables for configuration Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- php/README.md | 8 ++++++-- php/index.php | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/php/README.md b/php/README.md index 5b9e7b8..c293951 100644 --- a/php/README.md +++ b/php/README.md @@ -2,7 +2,11 @@ - Run `composer install` - Save your PEM file into the same directory and name it key.pem -- Edit the `index.php` file, replacing `` with your client SDK ID -- Optional - Update the endpoint in `index.php` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) +- Set environment variables or edit the `index.php` file: + - `SDK_ID`: Your client SDK ID (required) + - `BASE_URL`: API base URL (default: `https://api.yoti.com/ai/v1`) + - `ENDPOINT`: API endpoint (default: `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) + - `PEM_FILE_PATH`: Path to PEM file (default: `key.pem`) + - `IMAGE_PATH`: Path to test image (default: `./image.jpeg`) - Optional - Replace the `image.jpeg` with your own. - Run the project with `php index.php` diff --git a/php/index.php b/php/index.php index 75de1eb..467bf58 100644 --- a/php/index.php +++ b/php/index.php @@ -5,17 +5,24 @@ use Yoti\Http\RequestBuilder; use Yoti\Http\Payload; -$image = file_get_contents('./image.jpeg'); +// Configuration - update these values +$baseUrl = getenv('BASE_URL') ?: 'https://api.yoti.com/ai/v1'; +$endpoint = getenv('ENDPOINT') ?: 'age-antispoofing'; +$pemFilePath = getenv('PEM_FILE_PATH') ?: 'key.pem'; +$sdkId = getenv('SDK_ID') ?: ''; +$imagePath = getenv('IMAGE_PATH') ?: './image.jpeg'; + +$image = file_get_contents($imagePath); $payload = [ "data" => base64_encode($image) ]; $request = (new RequestBuilder()) - ->withBaseUrl('https://api.yoti.com/ai/v1') - ->withPemFilePath('key.pem') - ->withEndpoint('/age-antispoofing') + ->withBaseUrl($baseUrl) + ->withPemFilePath($pemFilePath) + ->withEndpoint('/' . $endpoint) ->withMethod('POST') ->withPayload(Payload::fromJsonData($payload)) - ->withHeader('X-Yoti-Auth-Id', '') + ->withHeader('X-Yoti-Auth-Id', $sdkId) ->build(); $response = $request->execute(); From 58816bb23b95342a7e0b6200ca53f03b204d8c44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:48:46 +0000 Subject: [PATCH 04/18] Fix code quality issues: improve error handling and indentation Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- go/main.go | 25 +++++++++++++++++++++---- php/index.php | 8 ++++++-- python/example.py | 2 +- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/go/main.go b/go/main.go index d65d176..6e7cd87 100644 --- a/go/main.go +++ b/go/main.go @@ -26,7 +26,12 @@ func main(){ keyFile := os.Getenv("PEM_FILE_PATH") imgPath := os.Getenv("TEST_IMAGE_PATH") - file, _ := os.Open(imgPath) + file, err := os.Open(imgPath) + if err != nil { + fmt.Println(err) + return + } + defer file.Close() reader := bufio.NewReader(file) content, _ := ioutil.ReadAll(reader) encoded := base64.StdEncoding.EncodeToString(content) @@ -40,9 +45,13 @@ func main(){ fmt.Println(err) } - key, _ := ioutil.ReadFile(keyFile) + key, err := ioutil.ReadFile(keyFile) + if err != nil { + fmt.Println(err) + return + } // Create request - req,_ := requests.SignedRequest{ + req, err := requests.SignedRequest{ HTTPMethod: http.MethodPost, BaseURL: baseURL, Endpoint: "/" + endpoint, @@ -53,9 +62,17 @@ func main(){ }, Body: jsonData, }.WithPemFile(key).Request() + if err != nil { + fmt.Println(err) + return + } //get Yoti response - response, _ := http.DefaultClient.Do(req) + response, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Println(err) + return + } buffer := new(strings.Builder) _, err = io.Copy(buffer, response.Body) diff --git a/php/index.php b/php/index.php index 467bf58..016fcb0 100644 --- a/php/index.php +++ b/php/index.php @@ -5,13 +5,17 @@ use Yoti\Http\RequestBuilder; use Yoti\Http\Payload; -// Configuration - update these values +// Configuration - update these values or set environment variables $baseUrl = getenv('BASE_URL') ?: 'https://api.yoti.com/ai/v1'; $endpoint = getenv('ENDPOINT') ?: 'age-antispoofing'; $pemFilePath = getenv('PEM_FILE_PATH') ?: 'key.pem'; -$sdkId = getenv('SDK_ID') ?: ''; +$sdkId = getenv('SDK_ID'); $imagePath = getenv('IMAGE_PATH') ?: './image.jpeg'; +if (!$sdkId) { + die("Error: SDK_ID environment variable is required. Please set SDK_ID or update the code directly.\n"); +} + $image = file_get_contents($imagePath); $payload = [ "data" => base64_encode($image) ]; diff --git a/python/example.py b/python/example.py index 42eabe5..671c755 100644 --- a/python/example.py +++ b/python/example.py @@ -32,7 +32,7 @@ def generate_session(): ) - # get Yoti response + # get Yoti response response = signed_request.execute() response_payload = json.loads(response.text) print(response_payload) From d21983b3838591a18c2bd5a3b36b91d4a6e3ccac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:50:07 +0000 Subject: [PATCH 05/18] Add comprehensive error handling to all examples Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- go/main.go | 9 +++++++-- php/index.php | 3 +++ python/example.py | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/go/main.go b/go/main.go index 6e7cd87..46f3c3e 100644 --- a/go/main.go +++ b/go/main.go @@ -33,16 +33,21 @@ func main(){ } defer file.Close() reader := bufio.NewReader(file) - content, _ := ioutil.ReadAll(reader) + content, err := ioutil.ReadAll(reader) + if err != nil { + fmt.Println(err) + return + } encoded := base64.StdEncoding.EncodeToString(content) estimation := &Estimation{ Data:encoded, } - jsonData,err := json.Marshal(estimation) + jsonData, err := json.Marshal(estimation) if err != nil { fmt.Println(err) + return } key, err := ioutil.ReadFile(keyFile) diff --git a/php/index.php b/php/index.php index 016fcb0..0cd7272 100644 --- a/php/index.php +++ b/php/index.php @@ -17,6 +17,9 @@ } $image = file_get_contents($imagePath); +if ($image === false) { + die("Error: Could not read image file: $imagePath\n"); +} $payload = [ "data" => base64_encode($image) ]; diff --git a/python/example.py b/python/example.py index 671c755..cac322f 100644 --- a/python/example.py +++ b/python/example.py @@ -13,6 +13,10 @@ def execute(request): return response.content def generate_session(): + endpoint = os.getenv('ENDPOINT') + if not endpoint: + raise ValueError("ENDPOINT environment variable is required") + with open(os.getenv('TEST_IMAGE_PATH'), "rb") as image_file: encoded_string = base64.b64encode(image_file.read()) data = {"data" : encoded_string.decode("utf-8")} @@ -24,7 +28,7 @@ def generate_session(): .builder() .with_pem_file(os.getenv('PEM_FILE_PATH')) .with_base_url(os.getenv('BASE_URL')) - .with_endpoint("/" + os.getenv('ENDPOINT')) + .with_endpoint("/" + endpoint) .with_http_method("POST") .with_header("X-Yoti-Auth-Id", os.getenv('SDK_ID')) .with_payload(payload_string) From 9f91bf66440c330b9715884a1c2ca4b5588e32f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:51:09 +0000 Subject: [PATCH 06/18] Fix Python endpoint default and Go formatting Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- go/main.go | 2 +- python/example.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/go/main.go b/go/main.go index 46f3c3e..63c91a2 100644 --- a/go/main.go +++ b/go/main.go @@ -41,7 +41,7 @@ func main(){ encoded := base64.StdEncoding.EncodeToString(content) estimation := &Estimation{ - Data:encoded, + Data: encoded, } jsonData, err := json.Marshal(estimation) diff --git a/python/example.py b/python/example.py index cac322f..738e60a 100644 --- a/python/example.py +++ b/python/example.py @@ -13,9 +13,7 @@ def execute(request): return response.content def generate_session(): - endpoint = os.getenv('ENDPOINT') - if not endpoint: - raise ValueError("ENDPOINT environment variable is required") + endpoint = os.getenv('ENDPOINT', 'age-antispoofing') with open(os.getenv('TEST_IMAGE_PATH'), "rb") as image_file: encoded_string = base64.b64encode(image_file.read()) From 34ceb742c6a56202e5da25104a0f77070184ddb6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:56:51 +0000 Subject: [PATCH 07/18] Complete migration to new Yoti AI Services API Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- .../obj/CoreExample.csproj.nuget.dgspec.json | 111 + .../obj/CoreExample.csproj.nuget.g.props | 15 + .../obj/CoreExample.csproj.nuget.g.targets | 2 + .../CoreExample/obj/project.assets.json | 5889 +++++++++++++++++ .../CoreExample/obj/project.nuget.cache | 124 + .../github.com/getyoti/yoti-go-sdk/@v/list | 2 + .../getyoti/yoti-go-sdk/@v/v1.1.0.mod | 1 + .../yoti-go-sdk/@v/v2.3.1+incompatible.info | 1 + .../yoti-go-sdk/@v/v2.3.1+incompatible.lock | 0 .../yoti-go-sdk/@v/v2.3.1+incompatible.mod | 1 + .../yoti-go-sdk/@v/v2.3.1+incompatible.zip | Bin 0 -> 170732 bytes .../@v/v2.3.1+incompatible.ziphash | 1 + .../github.com/getyoti/yoti-go-sdk/v3/@v/list | 1 + .../getyoti/yoti-go-sdk/v3/@v/v3.14.0.info | 1 + .../getyoti/yoti-go-sdk/v3/@v/v3.14.0.lock | 0 .../getyoti/yoti-go-sdk/v3/@v/v3.14.0.mod | 10 + .../getyoti/yoti-go-sdk/v3/@v/v3.14.0.zip | Bin 0 -> 630802 bytes .../getyoti/yoti-go-sdk/v3/@v/v3.14.0.ziphash | 1 + .../download/github.com/google/go-cmp/@v/list | 1 + .../github.com/google/go-cmp/@v/v0.5.5.lock | 0 .../github.com/google/go-cmp/@v/v0.5.5.mod | 5 + .../github.com/google/go-cmp/@v/v0.5.5.zip | Bin 0 -> 130974 bytes .../google/go-cmp/@v/v0.5.5.ziphash | 1 + .../download/github.com/joho/godotenv/@v/list | 1 + .../github.com/joho/godotenv/@v/v1.5.1.info | 1 + .../github.com/joho/godotenv/@v/v1.5.1.lock | 0 .../github.com/joho/godotenv/@v/v1.5.1.mod | 3 + .../github.com/joho/godotenv/@v/v1.5.1.zip | Bin 0 -> 18772 bytes .../joho/godotenv/@v/v1.5.1.ziphash | 1 + .../cache/download/gotest.tools/v3/@v/list | 1 + .../download/gotest.tools/v3/@v/v3.3.0.lock | 0 .../download/gotest.tools/v3/@v/v3.3.0.mod | 10 + .../download/gotest.tools/v3/@v/v3.3.0.zip | Bin 0 -> 98546 bytes .../gotest.tools/v3/@v/v3.3.0.ziphash | 1 + .../github.com/getyoti/yoti-go-sdk/v3@v3.14.0 | 9 + .../github.com/getyoti/yoti-go-sdk@v1.1.0 | 9 + .../getyoti/yoti-go-sdk@v2.3.1+incompatible | 9 + .../lookup/github.com/google/go-cmp@v0.5.5 | 9 + .../lookup/github.com/joho/godotenv@v1.5.1 | 9 + .../lookup/gotest.tools/v3@v3.3.0 | 9 + .../sumdb/sum.golang.org/tile/8/0/x001/019 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/0/x012/389 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/0/x043/108 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/0/x060/351 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/0/x151/793 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/0/x182/361 | Bin 0 -> 8192 bytes .../sum.golang.org/tile/8/0/x182/361.p/254 | Bin 0 -> 8128 bytes .../sumdb/sum.golang.org/tile/8/0/x182/362 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/0/x182/371 | Bin 0 -> 8192 bytes .../sum.golang.org/tile/8/0/x182/371.p/161 | Bin 0 -> 5152 bytes .../sum.golang.org/tile/8/0/x182/374.p/180 | Bin 0 -> 5760 bytes .../sumdb/sum.golang.org/tile/8/1/003 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/1/048 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/1/168 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/1/235 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/1/592 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/1/712.p/102 | Bin 0 -> 3264 bytes .../sumdb/sum.golang.org/tile/8/1/712.p/89 | Bin 0 -> 2848 bytes .../sumdb/sum.golang.org/tile/8/1/712.p/99 | Bin 0 -> 3168 bytes .../sumdb/sum.golang.org/tile/8/2/000 | Bin 0 -> 8192 bytes .../sumdb/sum.golang.org/tile/8/2/002.p/200 | Bin 0 -> 6400 bytes .../sumdb/sum.golang.org/tile/8/3/000.p/2 | 3 + root/pkg/mod/cache/lock | 0 .../yoti-go-sdk/v3@v3.14.0/.gitattributes | 2 + .../v3@v3.14.0/.github/ISSUE_TEMPLATE.md | 17 + .../v3@v3.14.0/.github/dependabot.yml | 12 + .../.github/workflows/codeql-analysis.yml | 57 + .../v3@v3.14.0/.github/workflows/sonar.yaml | 31 + .../v3@v3.14.0/.github/workflows/tests.yaml | 29 + .../getyoti/yoti-go-sdk/v3@v3.14.0/.gitignore | 23 + .../yoti-go-sdk/v3@v3.14.0/.golangci.yaml | 26 + .../v3@v3.14.0/.pre-commit-config.yaml | 40 + .../yoti-go-sdk/v3@v3.14.0/CONTRIBUTING.md | 9 + .../getyoti/yoti-go-sdk/v3@v3.14.0/LICENSE.md | 23 + .../getyoti/yoti-go-sdk/v3@v3.14.0/README.md | 70 + .../yoti-go-sdk/v3@v3.14.0/_docs/IDV.md | 9 + .../v3@v3.14.0/_docs/IDV_SANDBOX.md | 25 + .../yoti-go-sdk/v3@v3.14.0/_docs/PROFILE.md | 153 + .../v3@v3.14.0/_docs/PROFILE_SANDBOX.md | 30 + .../v3@v3.14.0/_docs/login_flow.png | Bin 0 -> 104616 bytes .../v3@v3.14.0/_examples/.gitignore | 9 + .../getyoti/yoti-go-sdk/v3@v3.14.0/aml/aml.go | 38 + .../yoti-go-sdk/v3@v3.14.0/aml/service.go | 55 + .../v3@v3.14.0/aml/service_test.go | 155 + .../getyoti/yoti-go-sdk/v3@v3.14.0/client.go | 87 + .../yoti-go-sdk/v3@v3.14.0/client_test.go | 205 + .../v3@v3.14.0/consts/attribute_names.go | 21 + .../yoti-go-sdk/v3@v3.14.0/consts/version.go | 6 + .../v3@v3.14.0/cryptoutil/crypto_utils.go | 165 + .../cryptoutil/crypto_utils_test.go | 176 + .../v3@v3.14.0/digital_identity_client.go | 88 + .../digital_identity_client_test.go | 168 + .../v3@v3.14.0/digitalidentity/address.go | 52 + .../digitalidentity/application_profile.go | 50 + .../attribute/age_verifications.go | 34 + .../attribute/age_verifications_test.go | 42 + .../attribute/anchor/anchor_parser.go | 110 + .../attribute/anchor/anchor_parser_test.go | 147 + .../attribute/anchor/anchors.go | 105 + .../attribute/anchor/anchors_test.go | 20 + .../attribute/anchor/signed_timestamp.go | 35 + .../attribute/attribute_details.go | 48 + .../attribute/attribute_test.go | 36 + .../attribute/date_attribute.go | 39 + .../attribute/date_attribute_test.go | 44 + .../digitalidentity/attribute/definition.go | 31 + .../attribute/definition_test.go | 18 + .../attribute/document_details_attribute.go | 87 + .../document_details_attribute_test.go | 185 + .../attribute/generic_attribute.go | 38 + .../attribute/generic_attribute_test.go | 39 + .../digitalidentity/attribute/helper_test.go | 21 + .../attribute/image_attribute.go | 53 + .../attribute/image_attribute_test.go | 106 + .../attribute/image_slice_attribute.go | 69 + .../attribute/image_slice_attribute_test.go | 61 + .../attribute/issuance_details.go | 86 + .../attribute/issuance_details_test.go | 145 + .../digitalidentity/attribute/item.go | 14 + .../attribute/json_attribute.go | 58 + .../attribute/json_attribute_test.go | 76 + .../attribute/multivalue_attribute.go | 90 + .../attribute/multivalue_attribute_test.go | 157 + .../digitalidentity/attribute/parser.go | 56 + .../digitalidentity/attribute/parser_test.go | 16 + .../attribute/string_attribute.go | 32 + .../attribute/string_attribute_test.go | 22 + .../digitalidentity/base_profile.go | 75 + .../digitalidentity/policy_builder.go | 261 + .../digitalidentity/policy_builder_test.go | 508 ++ .../v3@v3.14.0/digitalidentity/qr_code.go | 6 + .../v3@v3.14.0/digitalidentity/receipt.go | 32 + .../digitalidentity/receipt_item_key.go | 7 + .../digitalidentity/requests/client.go | 10 + .../digitalidentity/requests/request.go | 40 + .../digitalidentity/requests/request_test.go | 71 + .../requests/signed_message.go | 233 + .../requests/signed_message_test.go | 169 + .../v3@v3.14.0/digitalidentity/service.go | 316 + .../digitalidentity/service_test.go | 224 + .../digitalidentity/share_receipt.go | 25 + .../digitalidentity/share_retrieve_qr.go | 10 + .../digitalidentity/share_session.go | 21 + .../digitalidentity/share_session_builder.go | 75 + .../share_session_builder_test.go | 99 + .../digitalidentity/share_session_created.go | 8 + .../share_session_notification_builder.go | 62 + ...share_session_notification_builder_test.go | 95 + .../digitalidentity/share_session_qr_code.go | 14 + .../digitalidentity/source_constraint.go | 105 + .../digitalidentity/user_profile.go | 182 + .../digitalidentity/user_profile_test.go | 704 ++ .../digitalidentity/wanted_anchor_builder.go | 44 + .../wanted_anchor_builder_test.go | 25 + .../wanted_attribute_builder.go | 81 + .../digitalidentity/wanted_attribute_test.go | 154 + .../digitalidentity/yotierror/response.go | 56 + .../yotierror/response_test.go | 68 + .../yotierror/signed_requests.go | 8 + .../yoti-go-sdk/v3@v3.14.0/docscan/client.go | 438 ++ .../v3@v3.14.0/docscan/client_test.go | 868 +++ .../v3@v3.14.0/docscan/constants/constants.go | 45 + .../v3@v3.14.0/docscan/endpoint.go | 27 + .../v3@v3.14.0/docscan/sandbox/client.go | 132 + .../v3@v3.14.0/docscan/sandbox/client_test.go | 343 + .../docscan/sandbox/request/check/check.go | 42 + .../check/document_authenticity_check.go | 46 + .../check/document_authenticity_check_test.go | 53 + .../sandbox/request/check/document_check.go | 26 + .../check/document_face_match_check.go | 46 + .../check/document_face_match_check_test.go | 53 + .../request/check/document_text_data_check.go | 75 + .../check/document_text_data_check_test.go | 98 + .../check/id_document_comparison_check.go | 49 + .../id_document_comparison_check_test.go | 73 + .../sandbox/request/check/liveness_check.go | 24 + .../sandbox/request/check/report/breakdown.go | 72 + .../request/check/report/breakdown_test.go | 80 + .../request/check/report/recommendation.go | 57 + .../check/report/recommendation_test.go | 68 + .../request/check/static_liveness_check.go | 37 + .../check/static_liveness_check_test.go | 45 + .../supplementary_document_text_data_check.go | 75 + ...lementary_document_text_data_check_test.go | 98 + .../check/third_party_identity_check.go | 37 + .../check/third_party_identity_check_test.go | 45 + .../request/check/zoom_liveness_check.go | 37 + .../request/check/zoom_liveness_check_test.go | 45 + .../docscan/sandbox/request/check_reports.go | 105 + .../sandbox/request/check_reports_test.go | 130 + .../sandbox/request/filter/document_filter.go | 53 + .../request/filter/document_filter_test.go | 92 + .../sandbox/request/response_config.go | 48 + .../sandbox/request/response_config_test.go | 72 + .../sandbox/request/task/document_task.go | 23 + .../document_text_data_extraction_task.go | 94 + ...document_text_data_extraction_task_test.go | 150 + ...tary_document_text_data_extraction_task.go | 75 + ...document_text_data_extraction_task_test.go | 131 + .../task/text_data_extraction_reason.go | 49 + .../task/text_data_extraction_reason_test.go | 63 + .../text_data_extraction_recommendation.go | 56 + ...ext_data_extraction_recommendation_test.go | 91 + .../docscan/sandbox/request/task_results.go | 45 + .../sandbox/request/task_results_test.go | 42 + .../docscan/session/create/check/constants.go | 6 + .../create/check/document_authenticity.go | 75 + .../check/document_authenticity_test.go | 102 + .../create/check/document_comparison.go | 56 + .../create/check/document_comparison_test.go | 24 + .../create/check/face_comparison_check.go | 33 + .../check/face_comparison_check_builder.go | 39 + .../check/face_comparison_check_test.go | 72 + .../create/check/face_comparison_config.go | 6 + .../session/create/check/face_match.go | 77 + .../session/create/check/face_match_test.go | 64 + .../docscan/session/create/check/liveness.go | 98 + .../session/create/check/liveness_test.go | 64 + .../session/create/check/requested_check.go | 12 + .../create/check/third_party_identity.go | 55 + .../create/check/third_party_identity_test.go | 23 + .../create/check/watchlist_advanced_ca.go | 9 + .../check/watchlist_advanced_ca_custom.go | 120 + .../watchlist_advanced_ca_custom_test.go | 32 + ...watchlist_advanced_ca_matching_strategy.go | 48 + .../check/watchlist_advanced_ca_sources.go | 34 + .../check/watchlist_advanced_ca_yoti.go | 84 + .../check/watchlist_advanced_ca_yoti_test.go | 30 + .../create/check/watchlist_screening.go | 77 + .../create/check/watchlist_screening_test.go | 26 + .../docscan/session/create/constants.go | 6 + .../session/create/create_session_result.go | 8 + .../facecapture/face_capture_resource.go | 12 + .../facecapture/face_capture_resource_test.go | 15 + .../upload_face_capture_image_payload.go | 65 + .../upload_face_capture_image_payload_test.go | 72 + .../session/create/filter/constants.go | 12 + .../session/create/filter/document_filter.go | 6 + .../create/filter/document_restriction.go | 41 + .../filter/document_restriction_test.go | 64 + .../filter/document_restrictions_filter.go | 88 + .../document_restrictions_filter_test.go | 140 + .../filter/orthogonal_restrictions_filter.go | 109 + .../orthogonal_restrictions_filter_test.go | 158 + .../create/filter/required_document.go | 7 + .../create/filter/required_id_document.go | 49 + .../filter/required_id_document_test.go | 44 + .../filter/required_supplementary_document.go | 84 + .../required_supplementary_document_test.go | 207 + .../session/create/filter/restriction.go | 13 + .../docscan/session/create/import_token.go | 35 + .../session/create/import_token_test.go | 28 + .../session/create/notification_config.go | 75 + .../create/notification_config_test.go | 32 + .../session/create/objective/objective.go | 6 + .../create/objective/proof_of_address.go | 39 + .../create/objective/proof_of_address_test.go | 24 + .../docscan/session/create/sdk_config.go | 206 + .../docscan/session/create/sdk_config_test.go | 259 + .../docscan/session/create/session_spec.go | 195 + .../session/create/session_spec_test.go | 385 ++ .../docscan/session/create/task/constants.go | 6 + .../session/create/task/requested_task.go | 12 + .../task/supplementary_text_extraction.go | 80 + .../supplementary_text_extraction_test.go | 78 + .../session/create/task/text_extraction.go | 104 + .../create/task/text_extraction_test.go | 115 + .../advanced_identity_profile_preview.go | 7 + .../advanced_identity_profile_response.go | 10 + .../session/retrieve/breakdown_response.go | 8 + .../docscan/session/retrieve/ca_sources.go | 7 + .../session/retrieve/capture_response.go | 144 + .../session/retrieve/capture_response_test.go | 75 + .../session/retrieve/check_response.go | 68 + .../session/retrieve/details_response.go | 7 + .../retrieve/document_fields_response.go | 6 + .../retrieve/document_id_photo_response.go | 6 + .../expanded_document_fields_response.go | 6 + .../face_capture_resource_response.go | 18 + .../face_capture_resource_response_test.go | 32 + .../session/retrieve/face_map_response.go | 6 + .../retrieve/failure_reason_response.go | 14 + .../docscan/session/retrieve/file_response.go | 6 + .../session/retrieve/frame_response.go | 6 + .../retrieve/generated_check_response.go | 17 + .../session/retrieve/generated_media.go | 7 + .../session/retrieve/generated_profile.go | 6 + .../session/retrieve/get_session_result.go | 154 + .../retrieve/get_session_result_test.go | 310 + .../retrieve/id_document_resource_response.go | 45 + .../id_document_resource_response_test.go | 44 + ..._document_text_extraction_task_response.go | 11 + ...ment_text_extraction_task_response_test.go | 30 + .../retrieve/identity_profile_preview.go | 6 + .../retrieve/identity_profile_response.go | 10 + .../docscan/session/retrieve/import_token.go | 7 + .../retrieve/liveness_resource_response.go | 7 + .../session/retrieve/media_response.go | 11 + .../docscan/session/retrieve/page_response.go | 8 + .../retrieve/recommendation_response.go | 8 + .../session/retrieve/report_response.go | 8 + .../session/retrieve/required_resource.go | 23 + .../retrieve/required_resource_test.go | 73 + .../retrieve/required_resources_types.go | 57 + .../session/retrieve/resource_container.go | 70 + .../retrieve/resource_container_test.go | 43 + .../session/retrieve/resource_response.go | 7 + .../docscan/session/retrieve/search_config.go | 20 + .../session_configuration_response.go | 52 + .../session_configuration_response_test.go | 74 + .../static_liveness_resource_response.go | 11 + ...upplementary_document_resource_response.go | 50 + ...mentary_document_resource_response_test.go | 44 + ..._document_text_extraction_task_response.go | 11 + ...ment_text_extraction_task_response_test.go | 28 + .../docscan/session/retrieve/task_response.go | 56 + .../session/retrieve/task_response_test.go | 51 + .../docscan/session/retrieve/watchlist.go | 12 + .../zoom_liveness_resource_response.go | 8 + .../docscan/supported/supported_documents.go | 18 + .../v3@v3.14.0/dynamic/policy_builder.go | 261 + .../v3@v3.14.0/dynamic/policy_builder_test.go | 533 ++ .../v3@v3.14.0/dynamic/scenario_builder.go | 73 + .../dynamic/scenario_builder_test.go | 124 + .../yoti-go-sdk/v3@v3.14.0/dynamic/service.go | 55 + .../v3@v3.14.0/dynamic/service_test.go | 115 + .../v3@v3.14.0/dynamic/share_url.go | 16 + .../v3@v3.14.0/dynamic/source_constraint.go | 105 + .../dynamic/source_constraint_builder_test.go | 115 + .../dynamic/wanted_anchor_builder.go | 44 + .../dynamic/wanted_anchor_builder_test.go | 25 + .../dynamic/wanted_attribute_builder.go | 81 + .../dynamic/wanted_attribute_test.go | 154 + .../v3@v3.14.0/extension/extension.go | 46 + .../v3@v3.14.0/extension/extension_test.go | 24 + .../location_constraint_extension.go | 78 + .../location_constraint_extension_test.go | 27 + .../third_party_attribute_extension.go | 64 + .../third_party_attribute_extension_test.go | 142 + .../extension/transactional_flow_extension.go | 42 + .../transactional_flow_extension_test.go | 27 + .../v3@v3.14.0/extra/extra_data.go | 50 + .../v3@v3.14.0/extra/extra_data_test.go | 153 + .../yoti-go-sdk/v3@v3.14.0/file/file.go | 21 + .../yoti-go-sdk/v3@v3.14.0/file/file_test.go | 18 + .../getyoti/yoti-go-sdk/v3@v3.14.0/go.mod | 10 + .../getyoti/yoti-go-sdk/v3@v3.14.0/go.sum | 33 + .../yoti-go-sdk/v3@v3.14.0/media/image.go | 45 + .../v3@v3.14.0/media/image_test.go | 31 + .../v3@v3.14.0/media/mediavalue.go | 65 + .../v3@v3.14.0/media/mediavalue_test.go | 35 + .../v3@v3.14.0/profile/activity_details.go | 48 + .../yoti-go-sdk/v3@v3.14.0/profile/address.go | 52 + .../v3@v3.14.0/profile/application_profile.go | 50 + .../profile/attribute/age_verifications.go | 34 + .../attribute/age_verifications_test.go | 42 + .../profile/attribute/anchor/anchor_parser.go | 110 + .../attribute/anchor/anchor_parser_test.go | 147 + .../profile/attribute/anchor/anchors.go | 105 + .../profile/attribute/anchor/anchors_test.go | 20 + .../attribute/anchor/signed_timestamp.go | 35 + .../profile/attribute/attribute_details.go | 48 + .../profile/attribute/attribute_test.go | 36 + .../profile/attribute/date_attribute.go | 39 + .../profile/attribute/date_attribute_test.go | 44 + .../profile/attribute/definition.go | 31 + .../profile/attribute/definition_test.go | 18 + .../attribute/document_details_attribute.go | 87 + .../document_details_attribute_test.go | 185 + .../profile/attribute/generic_attribute.go | 38 + .../attribute/generic_attribute_test.go | 39 + .../profile/attribute/helper_test.go | 21 + .../profile/attribute/image_attribute.go | 53 + .../profile/attribute/image_attribute_test.go | 106 + .../attribute/image_slice_attribute.go | 69 + .../attribute/image_slice_attribute_test.go | 61 + .../profile/attribute/issuance_details.go | 86 + .../attribute/issuance_details_test.go | 145 + .../v3@v3.14.0/profile/attribute/item.go | 14 + .../profile/attribute/json_attribute.go | 58 + .../profile/attribute/json_attribute_test.go | 76 + .../profile/attribute/multivalue_attribute.go | 90 + .../attribute/multivalue_attribute_test.go | 157 + .../v3@v3.14.0/profile/attribute/parser.go | 56 + .../profile/attribute/parser_test.go | 16 + .../profile/attribute/string_attribute.go | 32 + .../attribute/string_attribute_test.go | 22 + .../v3@v3.14.0/profile/base_profile.go | 75 + .../v3@v3.14.0/profile/data_objects.go | 27 + .../v3@v3.14.0/profile/receipt_parser.go | 61 + .../v3@v3.14.0/profile/sandbox/anchor.go | 62 + .../v3@v3.14.0/profile/sandbox/anchor_test.go | 32 + .../v3@v3.14.0/profile/sandbox/attribute.go | 52 + .../profile/sandbox/attribute_test.go | 48 + .../v3@v3.14.0/profile/sandbox/client.go | 80 + .../v3@v3.14.0/profile/sandbox/client_test.go | 145 + .../profile/sandbox/document_images.go | 40 + .../profile/sandbox/document_images_test.go | 47 + .../profile/sandbox/tokenrequest.go | 133 + .../profile/sandbox/tokenrequest_test.go | 198 + .../yoti-go-sdk/v3@v3.14.0/profile/service.go | 145 + .../v3@v3.14.0/profile/service_test.go | 465 ++ .../v3@v3.14.0/profile/user_profile.go | 182 + .../v3@v3.14.0/profile/user_profile_test.go | 704 ++ .../yoti-go-sdk/v3@v3.14.0/requests/client.go | 10 + .../v3@v3.14.0/requests/request.go | 38 + .../v3@v3.14.0/requests/request_test.go | 90 + .../v3@v3.14.0/requests/signed_message.go | 215 + .../requests/signed_message_test.go | 169 + .../v3@v3.14.0/sh/go-build-modtidy.sh | 6 + .../yoti-go-sdk/v3@v3.14.0/sh/gofmt.sh | 13 + .../yoti-go-sdk/v3@v3.14.0/sh/goimports.sh | 8 + .../getyoti/yoti-go-sdk/v3@v3.14.0/sh/test.sh | 4 + .../v3@v3.14.0/sonar-project.properties | 15 + .../yoti-go-sdk/v3@v3.14.0/test/attribute.go | 41 + .../yoti-go-sdk/v3@v3.14.0/test/constants.go | 7 + .../v3@v3.14.0/test/decode_test_file.go | 19 + .../yoti-go-sdk/v3@v3.14.0/test/fileparser.go | 33 + ...sionResultWithAdvancedIdentityProfile.json | 51 + .../GetSessionResultWithIdentityProfile.json | 49 + .../fixtures/RTWIdentityProfileReport.json | 118 + .../fixtures/resource-container-static.json | 52 + .../test/fixtures/resource-container.json | 30 + .../fixtures/test_anchor_driving_license.txt | 1 + .../test/fixtures/test_anchor_passport.txt | 1 + .../test/fixtures/test_anchor_unknown.txt | 39 + .../test/fixtures/test_anchor_yoti_admin.txt | 1 + .../fixtures/test_attribute_date_of_birth.txt | 1 + .../fixtures/test_attribute_multivalue.txt | 1 + .../fixtures/test_attribute_third_party.txt | 1 + .../test/fixtures/test_extra_data.txt | 1 + .../test_third_party_issuance_details.txt | 1 + .../watchlist_advanced_ca_profile_custom.json | 257 + .../test/fixtures/watchlist_screening.json | 135 + .../yoti-go-sdk/v3@v3.14.0/test/key.go | 23 + .../test/test-key-invalid-format.pem | 51 + .../yoti-go-sdk/v3@v3.14.0/test/test-key.pem | 51 + .../yoti-go-sdk/v3@v3.14.0/util/conversion.go | 16 + .../v3@v3.14.0/yotierror/activitydetails.go | 14 + .../v3@v3.14.0/yotierror/multi_error.go | 21 + .../v3@v3.14.0/yotierror/multi_error_test.go | 17 + .../v3@v3.14.0/yotierror/response.go | 137 + .../v3@v3.14.0/yotierror/response_test.go | 151 + .../yotierror/sharing_failure_error.go | 10 + .../v3@v3.14.0/yotiprotoattr/Attribute.pb.go | 670 ++ .../yotiprotoattr/ContentType.pb.go | 164 + .../v3@v3.14.0/yotiprotoattr/List.pb.go | 306 + .../v3@v3.14.0/yotiprotoattr/Signing.pb.go | 224 + .../yotiprotocom/EncryptedData.pb.go | 167 + .../yotiprotocom/SignedTimestamp.pb.go | 210 + .../v3@v3.14.0/yotiprotoshare/DataEntry.pb.go | 242 + .../v3@v3.14.0/yotiprotoshare/ExtraData.pb.go | 162 + .../yotiprotoshare/IssuingAttributes.pb.go | 234 + .../yotiprotoshare/ThirdPartyAttribute.pb.go | 177 + .../.gitignore | 12 + .../CONTRIBUTING.md | 43 + .../LICENSE.md | 23 + .../yoti-go-sdk@v2.3.1+incompatible/README.md | 339 + .../activitydetails.go | 14 + .../activityerrors.go | 12 + .../yoti-go-sdk@v2.3.1+incompatible/aml.go | 49 + .../anchor/anchorparser.go | 114 + .../anchor/anchors.go | 105 + .../anchor/signedtimestamp.go | 35 + .../attribute/genericattribute.go | 84 + .../attribute/image.go | 33 + .../attribute/imageattribute.go | 67 + .../attribute/jsonattribute.go | 70 + .../attribute/stringattribute.go | 49 + .../attribute/timeattribute.go | 59 + .../conversion.go | 28 + .../yoti-go-sdk@v2.3.1+incompatible/crypto.go | 149 + .../dataobjects.go | 20 + .../examples/aml/.env.example | 2 + .../examples/aml/.gitignore | 1 + .../examples/aml/main.go | 64 + .../examples/profile/.env.example | 4 + .../examples/profile/.gitignore | 1 + .../examples/profile/certificatehelper.go | 175 + .../examples/profile/images/.keep | 0 .../examples/profile/login.html | 15 + .../examples/profile/main.go | 177 + .../examples/profile/profile.html | 95 + .../httprequester.go | 72 + .../yoti-go-sdk@v2.3.1+incompatible/image.go | 45 + .../login_flow.png | Bin 0 -> 104616 bytes .../test-key-invalid-format.pem | 51 + .../test-key.pem | 51 + .../testanchordrivinglicense.txt | 1 + .../testanchorpassport.txt | 1 + .../testanchoryotiadmin.txt | 1 + .../yoti_test.go | 1210 ++++ .../yotiattributevalue.go | 60 + .../yoticlient.go | 476 ++ .../yotiprofile.go | 151 + .../yotiprotoattr/Attribute.pb.go | 245 + .../yotiprotoattr/Attribute.proto | 57 + .../yotiprotoattr/List.pb.go | 174 + .../yotiprotoattr/List.proto | 28 + .../yotiprotoattr/Signing.pb.go | 127 + .../yotiprotoattr/Signing.proto | 24 + .../yotiprotocom/EncryptedData.pb.go | 91 + .../yotiprotocom/EncryptedData.proto | 17 + .../yotiprotocom/SignedTimestamp.pb.go | 147 + .../yotiprotocom/SignedTimestamp.proto | 45 + .../yotiuserprofile.go | 53 + .../go-cmp@v0.5.5/.github/workflows/test.yml | 30 + .../google/go-cmp@v0.5.5/CONTRIBUTING.md | 23 + .../github.com/google/go-cmp@v0.5.5/LICENSE | 27 + .../github.com/google/go-cmp@v0.5.5/README.md | 44 + .../go-cmp@v0.5.5/cmp/cmpopts/equate.go | 148 + .../go-cmp@v0.5.5/cmp/cmpopts/errors_go113.go | 15 + .../cmp/cmpopts/errors_xerrors.go | 18 + .../go-cmp@v0.5.5/cmp/cmpopts/example_test.go | 130 + .../go-cmp@v0.5.5/cmp/cmpopts/ignore.go | 206 + .../google/go-cmp@v0.5.5/cmp/cmpopts/sort.go | 147 + .../cmp/cmpopts/struct_filter.go | 187 + .../go-cmp@v0.5.5/cmp/cmpopts/util_test.go | 1371 ++++ .../google/go-cmp@v0.5.5/cmp/cmpopts/xform.go | 35 + .../google/go-cmp@v0.5.5/cmp/compare.go | 682 ++ .../google/go-cmp@v0.5.5/cmp/compare_test.go | 2900 ++++++++ .../cmp/example_reporter_test.go | 59 + .../google/go-cmp@v0.5.5/cmp/example_test.go | 376 ++ .../google/go-cmp@v0.5.5/cmp/export_panic.go | 15 + .../google/go-cmp@v0.5.5/cmp/export_unsafe.go | 35 + .../cmp/internal/diff/debug_disable.go | 17 + .../cmp/internal/diff/debug_enable.go | 122 + .../go-cmp@v0.5.5/cmp/internal/diff/diff.go | 398 ++ .../cmp/internal/diff/diff_test.go | 449 ++ .../go-cmp@v0.5.5/cmp/internal/flags/flags.go | 9 + .../cmp/internal/flags/toolchain_legacy.go | 10 + .../cmp/internal/flags/toolchain_recent.go | 10 + .../cmp/internal/function/func.go | 99 + .../cmp/internal/function/func_test.go | 51 + .../cmp/internal/testprotos/protos.go | 116 + .../cmp/internal/teststructs/foo1/foo.go | 10 + .../cmp/internal/teststructs/foo2/foo.go | 10 + .../cmp/internal/teststructs/project1.go | 267 + .../cmp/internal/teststructs/project2.go | 74 + .../cmp/internal/teststructs/project3.go | 82 + .../cmp/internal/teststructs/project4.go | 142 + .../cmp/internal/teststructs/structs.go | 197 + .../go-cmp@v0.5.5/cmp/internal/value/name.go | 157 + .../cmp/internal/value/name_test.go | 144 + .../cmp/internal/value/pointer_purego.go | 33 + .../cmp/internal/value/pointer_unsafe.go | 36 + .../go-cmp@v0.5.5/cmp/internal/value/sort.go | 106 + .../cmp/internal/value/sort_test.go | 159 + .../go-cmp@v0.5.5/cmp/internal/value/zero.go | 48 + .../cmp/internal/value/zero_test.go | 52 + .../google/go-cmp@v0.5.5/cmp/options.go | 552 ++ .../google/go-cmp@v0.5.5/cmp/options_test.go | 216 + .../google/go-cmp@v0.5.5/cmp/path.go | 378 ++ .../google/go-cmp@v0.5.5/cmp/report.go | 54 + .../go-cmp@v0.5.5/cmp/report_compare.go | 432 ++ .../go-cmp@v0.5.5/cmp/report_references.go | 264 + .../go-cmp@v0.5.5/cmp/report_reflect.go | 402 ++ .../google/go-cmp@v0.5.5/cmp/report_slices.go | 465 ++ .../google/go-cmp@v0.5.5/cmp/report_text.go | 431 ++ .../google/go-cmp@v0.5.5/cmp/report_value.go | 121 + .../google/go-cmp@v0.5.5/cmp/testdata/diffs | 1706 +++++ .../github.com/google/go-cmp@v0.5.5/go.mod | 5 + .../github.com/google/go-cmp@v0.5.5/go.sum | 2 + .../godotenv@v1.5.1/.github/dependabot.yml | 10 + .../godotenv@v1.5.1/.github/workflows/ci.yml | 20 + .../.github/workflows/codeql-analysis.yml | 72 + .../.github/workflows/release.yml | 31 + .../joho/godotenv@v1.5.1/.gitignore | 1 + .../github.com/joho/godotenv@v1.5.1/LICENCE | 23 + .../github.com/joho/godotenv@v1.5.1/README.md | 202 + .../joho/godotenv@v1.5.1/autoload/autoload.go | 15 + .../joho/godotenv@v1.5.1/cmd/godotenv/cmd.go | 56 + .../godotenv@v1.5.1/fixtures/comments.env | 4 + .../joho/godotenv@v1.5.1/fixtures/equals.env | 1 + .../godotenv@v1.5.1/fixtures/exported.env | 2 + .../godotenv@v1.5.1/fixtures/invalid1.env | 2 + .../joho/godotenv@v1.5.1/fixtures/plain.env | 8 + .../joho/godotenv@v1.5.1/fixtures/quoted.env | 19 + .../fixtures/substitutions.env | 5 + .../github.com/joho/godotenv@v1.5.1/go.mod | 3 + .../joho/godotenv@v1.5.1/godotenv.go | 228 + .../joho/godotenv@v1.5.1/godotenv_test.go | 575 ++ .../github.com/joho/godotenv@v1.5.1/parser.go | 271 + .../v3@v3.3.0/.circleci/config.yml | 63 + .../mod/gotest.tools/v3@v3.3.0/.codecov.yml | 8 + .../.github/workflows/sync-main.yaml | 18 + .../pkg/mod/gotest.tools/v3@v3.3.0/.gitignore | 5 + .../mod/gotest.tools/v3@v3.3.0/.golangci.yml | 68 + .../gotest.tools/v3@v3.3.0/CONTRIBUTING.md | 12 + .../pkg/mod/gotest.tools/v3@v3.3.0/Dockerfile | 33 + root/pkg/mod/gotest.tools/v3@v3.3.0/LICENSE | 13 + root/pkg/mod/gotest.tools/v3@v3.3.0/README.md | 53 + .../gotest.tools/v3@v3.3.0/assert/assert.go | 314 + .../v3@v3.3.0/assert/assert_ext_test.go | 112 + .../v3@v3.3.0/assert/assert_test.go | 470 ++ .../cmd/gty-migrate-from-testify/call.go | 201 + .../cmd/gty-migrate-from-testify/call_test.go | 35 + .../cmd/gty-migrate-from-testify/doc.go | 22 + .../cmd/gty-migrate-from-testify/main.go | 262 + .../cmd/gty-migrate-from-testify/main_test.go | 53 + .../cmd/gty-migrate-from-testify/migrate.go | 389 ++ .../gty-migrate-from-testify/migrate_test.go | 381 ++ .../testdata/full-expected/some_test.go | 150 + .../testdata/full/some_test.go | 151 + .../cmd/gty-migrate-from-testify/walktype.go | 31 + .../v3@v3.3.0/assert/cmp/compare.go | 394 ++ .../v3@v3.3.0/assert/cmp/compare_test.go | 680 ++ .../v3@v3.3.0/assert/cmp/result.go | 110 + .../v3@v3.3.0/assert/example_test.go | 26 + .../gotest.tools/v3@v3.3.0/assert/opt/opt.go | 118 + .../v3@v3.3.0/assert/opt/opt_test.go | 265 + .../pkg/mod/gotest.tools/v3@v3.3.0/env/env.go | 120 + .../gotest.tools/v3@v3.3.0/env/env_test.go | 170 + .../v3@v3.3.0/env/example_test.go | 18 + .../gotest.tools/v3@v3.3.0/fs/example_test.go | 70 + .../pkg/mod/gotest.tools/v3@v3.3.0/fs/file.go | 131 + .../gotest.tools/v3@v3.3.0/fs/file_test.go | 118 + .../mod/gotest.tools/v3@v3.3.0/fs/manifest.go | 139 + .../v3@v3.3.0/fs/manifest_test.go | 110 + .../v3@v3.3.0/fs/manifest_unix.go | 38 + .../v3@v3.3.0/fs/manifest_windows.go | 22 + root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops.go | 275 + .../mod/gotest.tools/v3@v3.3.0/fs/ops_test.go | 100 + .../pkg/mod/gotest.tools/v3@v3.3.0/fs/path.go | 199 + .../mod/gotest.tools/v3@v3.3.0/fs/report.go | 276 + .../gotest.tools/v3@v3.3.0/fs/report_test.go | 273 + .../fs/testdata/copy-test-with-symlink/1 | 1 + .../fs/testdata/copy-test-with-symlink/a/1 | 1 + .../fs/testdata/copy-test-with-symlink/a/2 | 1 + .../fs/testdata/copy-test-with-symlink/a/b/1 | 1 + .../v3@v3.3.0/fs/testdata/copy-test/1 | 1 + .../v3@v3.3.0/fs/testdata/copy-test/a/1 | 1 + .../v3@v3.3.0/fs/testdata/copy-test/a/2 | 1 + .../v3@v3.3.0/fs/testdata/copy-test/a/b/1 | 1 + root/pkg/mod/gotest.tools/v3@v3.3.0/go.mod | 10 + root/pkg/mod/gotest.tools/v3@v3.3.0/go.sum | 31 + .../v3@v3.3.0/golden/example_test.go | 22 + .../gotest.tools/v3@v3.3.0/golden/golden.go | 190 + .../v3@v3.3.0/golden/golden_test.go | 296 + .../gotest.tools/v3@v3.3.0/icmd/command.go | 290 + .../v3@v3.3.0/icmd/command_test.go | 181 + .../v3@v3.3.0/icmd/example_test.go | 22 + .../gotest.tools/v3@v3.3.0/icmd/exitcode.go | 24 + .../v3@v3.3.0/icmd/internal/stub/main.go | 26 + .../mod/gotest.tools/v3@v3.3.0/icmd/ops.go | 46 + .../v3@v3.3.0/internal/assert/assert.go | 160 + .../v3@v3.3.0/internal/assert/result.go | 146 + .../v3@v3.3.0/internal/cleanup/cleanup.go | 48 + .../v3@v3.3.0/internal/difflib/LICENSE | 27 + .../v3@v3.3.0/internal/difflib/difflib.go | 423 ++ .../v3@v3.3.0/internal/format/diff.go | 161 + .../v3@v3.3.0/internal/format/diff_test.go | 71 + .../v3@v3.3.0/internal/format/format.go | 27 + .../v3@v3.3.0/internal/format/format_test.go | 62 + .../many-diff-no-trailing-newline.golden | 13 + .../internal/format/testdata/many-diff.golden | 12 + .../testdata/one-diff-with-header.golden | 7 + .../format/testdata/whitespace-diff.golden | 7 + .../v3@v3.3.0/internal/maint/maint.go | 28 + .../v3@v3.3.0/internal/source/defers.go | 52 + .../v3@v3.3.0/internal/source/source.go | 147 + .../v3@v3.3.0/internal/source/source_test.go | 94 + .../v3@v3.3.0/internal/source/update.go | 138 + .../v3@v3.3.0/internal/source/version.go | 35 + root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go | 4 + .../mod/gotest.tools/v3@v3.3.0/poll/check.go | 47 + .../gotest.tools/v3@v3.3.0/poll/check_test.go | 42 + .../v3@v3.3.0/poll/example_test.go | 45 + .../mod/gotest.tools/v3@v3.3.0/poll/poll.go | 171 + .../gotest.tools/v3@v3.3.0/poll/poll_test.go | 87 + .../scripts/binary-gty-migrate-from-testify | 2 + .../gotest.tools/v3@v3.3.0/scripts/test-unit | 7 + .../v3@v3.3.0/skip/example_test.go | 43 + .../mod/gotest.tools/v3@v3.3.0/skip/skip.go | 89 + .../gotest.tools/v3@v3.3.0/skip/skip_test.go | 136 + root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go | 5 + .../v3@v3.3.0/x/subtest/context.go | 93 + .../v3@v3.3.0/x/subtest/context_test.go | 32 + .../v3@v3.3.0/x/subtest/example_test.go | 66 + root/pkg/sumdb/sum.golang.org/latest | 5 + .../getyoti/age-scan-examples/go.sum | 8 + 681 files changed, 67204 insertions(+) create mode 100644 dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json create mode 100644 dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props create mode 100644 dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets create mode 100644 dotnet/CoreExample/CoreExample/obj/project.assets.json create mode 100644 dotnet/CoreExample/CoreExample/obj/project.nuget.cache create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/list create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v1.1.0.mod create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.info create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.lock create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.mod create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.zip create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.ziphash create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/list create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.info create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.lock create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.mod create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.zip create mode 100644 root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.ziphash create mode 100644 root/pkg/mod/cache/download/github.com/google/go-cmp/@v/list create mode 100644 root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.lock create mode 100644 root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.mod create mode 100644 root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.zip create mode 100644 root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.ziphash create mode 100644 root/pkg/mod/cache/download/github.com/joho/godotenv/@v/list create mode 100644 root/pkg/mod/cache/download/github.com/joho/godotenv/@v/v1.5.1.info create mode 100644 root/pkg/mod/cache/download/github.com/joho/godotenv/@v/v1.5.1.lock create mode 100644 root/pkg/mod/cache/download/github.com/joho/godotenv/@v/v1.5.1.mod create mode 100644 root/pkg/mod/cache/download/github.com/joho/godotenv/@v/v1.5.1.zip create mode 100644 root/pkg/mod/cache/download/github.com/joho/godotenv/@v/v1.5.1.ziphash create mode 100644 root/pkg/mod/cache/download/gotest.tools/v3/@v/list create mode 100644 root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.lock create mode 100644 root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.mod create mode 100644 root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.zip create mode 100644 root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.ziphash create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk/v3@v3.14.0 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v1.1.0 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/google/go-cmp@v0.5.5 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/joho/godotenv@v1.5.1 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gotest.tools/v3@v3.3.0 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x001/019 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x012/389 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x043/108 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x060/351 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x151/793 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/361 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/361.p/254 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/362 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/371 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/371.p/161 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/374.p/180 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/003 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/048 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/168 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/235 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/592 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/102 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/89 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/99 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/2/000 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/2/002.p/200 create mode 100644 root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/3/000.p/2 create mode 100644 root/pkg/mod/cache/lock create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.gitattributes create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/ISSUE_TEMPLATE.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/dependabot.yml create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/workflows/codeql-analysis.yml create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/workflows/sonar.yaml create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/workflows/tests.yaml create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.gitignore create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.golangci.yaml create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.pre-commit-config.yaml create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/CONTRIBUTING.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/LICENSE.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/README.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV_SANDBOX.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE_SANDBOX.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/login_flow.png create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_examples/.gitignore create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/aml/aml.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/aml/service.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/aml/service_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/client_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/consts/attribute_names.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/consts/version.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/cryptoutil/crypto_utils.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/cryptoutil/crypto_utils_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/address.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/application_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/signed_timestamp.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_details.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/helper_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/item.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/base_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/qr_code.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt_item_key.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_receipt.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_retrieve_qr.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_created.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_qr_code.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/source_constraint.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/signed_requests.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/constants/constants.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/endpoint.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/liveness_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_task.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/constants.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_config.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/requested_check.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_matching_strategy.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_sources.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/constants.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/create_session_result.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/constants.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_filter.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_document.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/restriction.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/objective.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/constants.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/requested_task.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_preview.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/breakdown_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/ca_sources.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/check_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/details_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_fields_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_id_photo_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/expanded_document_fields_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_map_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/failure_reason_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/file_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/frame_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_check_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_media.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_preview.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/import_token.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/liveness_resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/media_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/page_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/recommendation_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/report_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resources_types.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/search_config.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/static_liveness_resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/watchlist.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/zoom_liveness_resource_response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/supported/supported_documents.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/share_url.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_builder.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.mod create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.sum create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/activity_details.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/address.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/application_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/signed_timestamp.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_details.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/helper_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/item.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/base_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/data_objects.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/receipt_parser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/client.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/go-build-modtidy.sh create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/gofmt.sh create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/goimports.sh create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/test.sh create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sonar-project.properties create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/attribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/constants.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/decode_test_file.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fileparser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithAdvancedIdentityProfile.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithIdentityProfile.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/RTWIdentityProfileReport.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container-static.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_driving_license.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_passport.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_unknown.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_yoti_admin.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_date_of_birth.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_multivalue.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_third_party.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_extra_data.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_third_party_issuance_details.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_advanced_ca_profile_custom.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_screening.json create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/key.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key-invalid-format.pem create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key.pem create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/util/conversion.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/activitydetails.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/sharing_failure_error.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Attribute.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/ContentType.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/List.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Signing.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/EncryptedData.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/SignedTimestamp.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/DataEntry.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ExtraData.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/IssuingAttributes.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ThirdPartyAttribute.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/.gitignore create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/CONTRIBUTING.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/LICENSE.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/README.md create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activitydetails.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activityerrors.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/aml.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchorparser.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchors.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/signedtimestamp.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/genericattribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/image.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/imageattribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/jsonattribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/stringattribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/timeattribute.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/conversion.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/crypto.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/dataobjects.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.env.example create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.gitignore create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/main.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.env.example create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.gitignore create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/certificatehelper.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/images/.keep create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/login.html create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/main.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/profile.html create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/httprequester.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/image.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/login_flow.png create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/test-key-invalid-format.pem create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/test-key.pem create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/testanchordrivinglicense.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/testanchorpassport.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/testanchoryotiadmin.txt create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yoti_test.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiattributevalue.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yoticlient.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprofile.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.proto create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.proto create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.proto create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.proto create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.pb.go create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.proto create mode 100644 root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiuserprofile.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/.github/workflows/test.yml create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/CONTRIBUTING.md create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/LICENSE create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/README.md create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/equate.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_go113.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_xerrors.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/example_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/ignore.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/sort.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/struct_filter.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/util_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/xform.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_reporter_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_panic.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_unsafe.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_disable.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_enable.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/flags.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_legacy.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_recent.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/testprotos/protos.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo1/foo.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo2/foo.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project1.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project2.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project3.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project4.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/structs.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_purego.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_unsafe.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options_test.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/path.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_compare.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_references.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_reflect.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_slices.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_text.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_value.go create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/testdata/diffs create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.mod create mode 100644 root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.sum create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/dependabot.yml create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/ci.yml create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/codeql-analysis.yml create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/release.yml create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/.gitignore create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/LICENCE create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/README.md create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/autoload/autoload.go create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/cmd/godotenv/cmd.go create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/comments.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/equals.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/exported.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/invalid1.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/plain.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/quoted.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/substitutions.env create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/go.mod create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv.go create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv_test.go create mode 100644 root/pkg/mod/github.com/joho/godotenv@v1.5.1/parser.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/.circleci/config.yml create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/.codecov.yml create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/.github/workflows/sync-main.yaml create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/.gitignore create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/.golangci.yml create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/CONTRIBUTING.md create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/Dockerfile create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/LICENSE create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/README.md create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_ext_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/doc.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full-expected/some_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full/some_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/walktype.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/result.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/env/env.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/env/env_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/env/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_unix.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_windows.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/path.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/1 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/1 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/2 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/b/1 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/1 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/1 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/2 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/b/1 create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/go.mod create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/go.sum create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/golden/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/update.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/version.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go create mode 100644 root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go create mode 100644 root/pkg/sumdb/sum.golang.org/latest create mode 100644 root/src/github.com/getyoti/age-scan-examples/go.sum diff --git a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json new file mode 100644 index 0000000..4479729 --- /dev/null +++ b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json @@ -0,0 +1,111 @@ +{ + "format": 1, + "restore": { + "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj": {} + }, + "projects": { + "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", + "projectName": "CoreExample", + "projectPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", + "packagesPath": "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages", + "outputPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/obj/", + "projectStyle": "PackageReference", + "configFilePaths": [ + "/home/runner/.nuget/NuGet/NuGet.Config" + ], + "originalTargetFrameworks": [ + "netcoreapp3.1" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "netcoreapp3.1": { + "targetAlias": "netcoreapp3.1", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "10.0.100" + }, + "frameworks": { + "netcoreapp3.1": { + "targetAlias": "netcoreapp3.1", + "dependencies": { + "DotNetEnv": { + "target": "Package", + "version": "[1.4.0, )" + }, + "Microsoft.AspNetCore.Hosting": { + "target": "Package", + "version": "[2.2.7, )" + }, + "Microsoft.Extensions.DependencyInjection": { + "target": "Package", + "version": "[3.1.8, )" + }, + "Microsoft.Extensions.Logging": { + "target": "Package", + "version": "[3.1.8, )" + }, + "Microsoft.Extensions.Logging.Console": { + "target": "Package", + "version": "[3.1.8, )" + }, + "Yoti": { + "target": "Package", + "version": "[3.6.0, )" + }, + "dotenv.net": { + "target": "Package", + "version": "[2.1.1, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "downloadDependencies": [ + { + "name": "Microsoft.AspNetCore.App.Ref", + "version": "[3.1.10, 3.1.10]" + }, + { + "name": "Microsoft.NETCore.App.Host.linux-x64", + "version": "[3.1.32, 3.1.32]" + }, + { + "name": "Microsoft.NETCore.App.Ref", + "version": "[3.1.0, 3.1.0]" + } + ], + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/10.0.100/RuntimeIdentifierGraph.json" + } + } + } + } +} \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props new file mode 100644 index 0000000..2e49d4b --- /dev/null +++ b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props @@ -0,0 +1,15 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + /home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages + /home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages + PackageReference + 7.0.0 + + + + + \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets new file mode 100644 index 0000000..3dc06ef --- /dev/null +++ b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/project.assets.json b/dotnet/CoreExample/CoreExample/obj/project.assets.json new file mode 100644 index 0000000..28236f4 --- /dev/null +++ b/dotnet/CoreExample/CoreExample/obj/project.assets.json @@ -0,0 +1,5889 @@ +{ + "version": 3, + "targets": { + ".NETCoreApp,Version=v3.1": { + "dotenv.net/2.1.1": { + "type": "package", + "dependencies": { + "System.Memory": "4.5.4" + }, + "compile": { + "lib/netstandard2.0/dotenv.net.dll": {} + }, + "runtime": { + "lib/netstandard2.0/dotenv.net.dll": {} + } + }, + "DotNetEnv/1.4.0": { + "type": "package", + "dependencies": { + "NETStandard.Library": "1.6.1" + }, + "compile": { + "lib/netstandard1.3/DotNetEnv.dll": {} + }, + "runtime": { + "lib/netstandard1.3/DotNetEnv.dll": {} + } + }, + "Google.Protobuf/3.12.3": { + "type": "package", + "dependencies": { + "System.Memory": "4.5.2" + }, + "compile": { + "lib/netstandard2.0/Google.Protobuf.dll": { + "related": ".pdb;.xml" + } + }, + "runtime": { + "lib/netstandard2.0/Google.Protobuf.dll": { + "related": ".pdb;.xml" + } + } + }, + "JsonSubTypes/1.7.0": { + "type": "package", + "dependencies": { + "Newtonsoft.Json": "12.0.3" + }, + "compile": { + "lib/netstandard2.0/JsonSubTypes.dll": {} + }, + "runtime": { + "lib/netstandard2.0/JsonSubTypes.dll": {} + } + }, + "Microsoft.AspNetCore.Hosting/2.2.7": { + "type": "package", + "dependencies": { + "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", + "Microsoft.AspNetCore.Http": "2.2.0", + "Microsoft.AspNetCore.Http.Extensions": "2.2.0", + "Microsoft.Extensions.Configuration": "2.2.0", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.2.4", + "Microsoft.Extensions.Configuration.FileExtensions": "2.2.0", + "Microsoft.Extensions.DependencyInjection": "2.2.0", + "Microsoft.Extensions.FileProviders.Physical": "2.2.0", + "Microsoft.Extensions.Hosting.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "System.Diagnostics.DiagnosticSource": "4.5.1", + "System.Reflection.Metadata": "1.6.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.2.0", + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.Extensions.Hosting.Abstractions": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.Http/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.AspNetCore.WebUtilities": "2.2.0", + "Microsoft.Extensions.ObjectPool": "2.2.0", + "Microsoft.Extensions.Options": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.Http.Extensions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", + "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Buffers": "4.5.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.Http.Features/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { + "related": ".xml" + } + } + }, + "Microsoft.AspNetCore.WebUtilities/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Configuration/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Configuration.Abstractions/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Primitives": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Configuration.Binder/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables/2.2.4": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Configuration.FileExtensions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.2.0", + "Microsoft.Extensions.FileProviders.Physical": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.DependencyInjection/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/3.1.8": { + "type": "package", + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.FileProviders.Abstractions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.FileProviders.Physical/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", + "Microsoft.Extensions.FileSystemGlobbing": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.FileSystemGlobbing/2.2.0": { + "type": "package", + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Hosting.Abstractions/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", + "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", + "Microsoft.Extensions.Logging.Abstractions": "2.2.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Logging/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "3.1.8", + "Microsoft.Extensions.DependencyInjection": "3.1.8", + "Microsoft.Extensions.Logging.Abstractions": "3.1.8", + "Microsoft.Extensions.Options": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Logging.Abstractions/3.1.8": { + "type": "package", + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Logging.Configuration/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Logging": "3.1.8", + "Microsoft.Extensions.Options.ConfigurationExtensions": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Logging.Console/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "3.1.8", + "Microsoft.Extensions.Logging": "3.1.8", + "Microsoft.Extensions.Logging.Configuration": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.ObjectPool/2.2.0": { + "type": "package", + "compile": { + "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Options/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8", + "Microsoft.Extensions.Primitives": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions/3.1.8": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "3.1.8", + "Microsoft.Extensions.Configuration.Binder": "3.1.8", + "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8", + "Microsoft.Extensions.Options": "3.1.8" + }, + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Extensions.Primitives/3.1.8": { + "type": "package", + "compile": { + "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.dll": { + "related": ".xml" + } + } + }, + "Microsoft.Net.Http.Headers/2.2.0": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0", + "System.Buffers": "4.5.0" + }, + "compile": { + "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": { + "related": ".xml" + } + } + }, + "Microsoft.NETCore.Platforms/1.1.1": { + "type": "package", + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "Microsoft.NETCore.Targets/1.1.0": { + "type": "package", + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "Microsoft.Win32.Primitives/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/Microsoft.Win32.Primitives.dll": { + "related": ".xml" + } + } + }, + "NETStandard.Library/1.6.1": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json/12.0.3": { + "type": "package", + "compile": { + "lib/netstandard2.0/Newtonsoft.Json.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/Newtonsoft.Json.dll": { + "related": ".xml" + } + } + }, + "NLog/4.7.2": { + "type": "package", + "compile": { + "lib/netstandard2.0/NLog.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/NLog.dll": { + "related": ".xml" + } + } + }, + "Portable.BouncyCastle/1.8.5": { + "type": "package", + "compile": { + "lib/netstandard2.0/BouncyCastle.Crypto.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/BouncyCastle.Crypto.dll": { + "related": ".xml" + } + } + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "debian.8-x64" + } + } + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "fedora.23-x64" + } + } + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "fedora.24-x64" + } + } + }, + "runtime.native.System/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "runtime.native.System.IO.Compression/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "runtime.native.System.Net.Http/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "runtime.native.System.Security.Cryptography.Apple/4.3.0": { + "type": "package", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + }, + "compile": { + "lib/netstandard1.0/_._": {} + }, + "runtime": { + "lib/netstandard1.0/_._": {} + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "opensuse.13.2-x64" + } + } + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "opensuse.42.1-x64" + } + } + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { + "type": "package", + "runtimeTargets": { + "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib": { + "assetType": "native", + "rid": "osx.10.10-x64" + } + } + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib": { + "assetType": "native", + "rid": "osx.10.10-x64" + } + } + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "rhel.7-x64" + } + } + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "ubuntu.14.04-x64" + } + } + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "ubuntu.16.04-x64" + } + } + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "type": "package", + "runtimeTargets": { + "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { + "assetType": "native", + "rid": "ubuntu.16.10-x64" + } + } + }, + "System.AppContext/4.3.0": { + "type": "package", + "dependencies": { + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.6/System.AppContext.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.6/System.AppContext.dll": {} + } + }, + "System.Buffers/4.5.0": { + "type": "package", + "compile": { + "ref/netcoreapp2.0/_._": {} + }, + "runtime": { + "lib/netcoreapp2.0/_._": {} + } + }, + "System.Collections/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Collections.dll": { + "related": ".xml" + } + } + }, + "System.Collections.Concurrent/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Collections.Concurrent.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Collections.Concurrent.dll": {} + } + }, + "System.Console/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Console.dll": { + "related": ".xml" + } + } + }, + "System.Diagnostics.Debug/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Diagnostics.Debug.dll": { + "related": ".xml" + } + } + }, + "System.Diagnostics.DiagnosticSource/4.5.1": { + "type": "package", + "compile": { + "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": { + "related": ".xml" + } + } + }, + "System.Diagnostics.Tools/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.0/System.Diagnostics.Tools.dll": { + "related": ".xml" + } + } + }, + "System.Diagnostics.Tracing/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.5/System.Diagnostics.Tracing.dll": { + "related": ".xml" + } + } + }, + "System.Globalization/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Globalization.dll": { + "related": ".xml" + } + } + }, + "System.Globalization.Calendars/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Globalization.Calendars.dll": { + "related": ".xml" + } + } + }, + "System.Globalization.Extensions/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/_._": { + "related": ".xml" + } + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.IO/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "ref/netstandard1.5/System.IO.dll": { + "related": ".xml" + } + } + }, + "System.IO.Compression/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.IO.Compression.dll": { + "related": ".xml" + } + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.IO.Compression.ZipFile/4.3.0": { + "type": "package", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.IO.Compression.ZipFile.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.IO.Compression.ZipFile.dll": {} + } + }, + "System.IO.FileSystem/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.IO.FileSystem.dll": { + "related": ".xml" + } + } + }, + "System.IO.FileSystem.Primitives/4.3.0": { + "type": "package", + "dependencies": { + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll": {} + } + }, + "System.Linq/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + }, + "compile": { + "ref/netstandard1.6/System.Linq.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.6/System.Linq.dll": {} + } + }, + "System.Linq.Expressions/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + }, + "compile": { + "ref/netstandard1.6/System.Linq.Expressions.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.6/System.Linq.Expressions.dll": {} + } + }, + "System.Memory/4.5.4": { + "type": "package", + "compile": { + "ref/netcoreapp2.1/_._": {} + }, + "runtime": { + "lib/netcoreapp2.1/_._": {} + } + }, + "System.Net.Http/4.3.4": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.1", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" + }, + "compile": { + "ref/netstandard1.3/System.Net.Http.dll": {} + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.3/System.Net.Http.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Net.Primitives/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Net.Primitives.dll": { + "related": ".xml" + } + } + }, + "System.Net.Sockets/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Net.Sockets.dll": { + "related": ".xml" + } + } + }, + "System.ObjectModel/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.ObjectModel.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.ObjectModel.dll": {} + } + }, + "System.Reflection/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.5/System.Reflection.dll": { + "related": ".xml" + } + } + }, + "System.Reflection.Emit/4.3.0": { + "type": "package", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.1/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Reflection.Emit.dll": {} + } + }, + "System.Reflection.Emit.ILGeneration/4.3.0": { + "type": "package", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.0/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll": {} + } + }, + "System.Reflection.Emit.Lightweight/4.3.0": { + "type": "package", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.0/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll": {} + } + }, + "System.Reflection.Extensions/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.0/System.Reflection.Extensions.dll": { + "related": ".xml" + } + } + }, + "System.Reflection.Metadata/1.6.0": { + "type": "package", + "compile": { + "lib/netstandard2.0/System.Reflection.Metadata.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/System.Reflection.Metadata.dll": { + "related": ".xml" + } + } + }, + "System.Reflection.Primitives/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.0/System.Reflection.Primitives.dll": { + "related": ".xml" + } + } + }, + "System.Reflection.TypeExtensions/4.3.0": { + "type": "package", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.5/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.5/System.Reflection.TypeExtensions.dll": {} + } + }, + "System.Resources.ResourceManager/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.0/System.Resources.ResourceManager.dll": { + "related": ".xml" + } + } + }, + "System.Runtime/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + }, + "compile": { + "ref/netstandard1.5/System.Runtime.dll": { + "related": ".xml" + } + } + }, + "System.Runtime.Extensions/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.5/System.Runtime.Extensions.dll": { + "related": ".xml" + } + } + }, + "System.Runtime.Handles/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Runtime.Handles.dll": { + "related": ".xml" + } + } + }, + "System.Runtime.InteropServices/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + }, + "compile": { + "ref/netcoreapp1.1/System.Runtime.InteropServices.dll": {} + } + }, + "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { + "type": "package", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + }, + "compile": { + "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} + }, + "runtime": { + "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Runtime.Numerics/4.3.0": { + "type": "package", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + }, + "compile": { + "ref/netstandard1.1/System.Runtime.Numerics.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Runtime.Numerics.dll": {} + } + }, + "System.Security.Cryptography.Algorithms/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + }, + "compile": { + "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll": {} + }, + "runtimeTargets": { + "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { + "assetType": "runtime", + "rid": "osx" + }, + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Security.Cryptography.Cng/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0" + }, + "compile": { + "ref/netstandard1.6/_._": {} + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Cng.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Security.Cryptography.Csp/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/_._": {} + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Security.Cryptography.Encoding/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll": { + "related": ".xml" + } + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Security.Cryptography.OpenSsl/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + }, + "compile": { + "ref/netstandard1.6/_._": {} + }, + "runtime": { + "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": {} + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": { + "assetType": "runtime", + "rid": "unix" + } + } + }, + "System.Security.Cryptography.Primitives/4.3.0": { + "type": "package", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} + }, + "runtime": { + "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} + } + }, + "System.Security.Cryptography.X509Certificates/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + }, + "compile": { + "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll": { + "related": ".xml" + } + }, + "runtimeTargets": { + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { + "assetType": "runtime", + "rid": "unix" + }, + "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { + "assetType": "runtime", + "rid": "win" + } + } + }, + "System.Text.Encoding/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Text.Encoding.dll": { + "related": ".xml" + } + } + }, + "System.Text.Encoding.Extensions/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Text.Encoding.Extensions.dll": { + "related": ".xml" + } + } + }, + "System.Text.Encodings.Web/4.5.0": { + "type": "package", + "compile": { + "lib/netstandard2.0/System.Text.Encodings.Web.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/System.Text.Encodings.Web.dll": { + "related": ".xml" + } + } + }, + "System.Text.RegularExpressions/4.3.0": { + "type": "package", + "dependencies": { + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netcoreapp1.1/System.Text.RegularExpressions.dll": {} + }, + "runtime": { + "lib/netstandard1.6/System.Text.RegularExpressions.dll": {} + } + }, + "System.Threading/4.3.0": { + "type": "package", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Threading.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Threading.dll": {} + } + }, + "System.Threading.Tasks/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Threading.Tasks.dll": { + "related": ".xml" + } + } + }, + "System.Threading.Tasks.Extensions/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + }, + "compile": { + "lib/netstandard1.0/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll": { + "related": ".xml" + } + } + }, + "System.Threading.Timer/4.3.0": { + "type": "package", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + }, + "compile": { + "ref/netstandard1.2/System.Threading.Timer.dll": { + "related": ".xml" + } + } + }, + "System.Xml.ReaderWriter/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Xml.ReaderWriter.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Xml.ReaderWriter.dll": {} + } + }, + "System.Xml.XDocument/4.3.0": { + "type": "package", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + }, + "compile": { + "ref/netstandard1.3/System.Xml.XDocument.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard1.3/System.Xml.XDocument.dll": {} + } + }, + "Yoti/3.6.0": { + "type": "package", + "dependencies": { + "Google.Protobuf": "3.12.3", + "JsonSubTypes": "1.7.0", + "NLog": "4.7.2", + "Newtonsoft.Json": "12.0.3", + "Portable.BouncyCastle": "1.8.5", + "System.Net.Http": "4.3.4" + }, + "compile": { + "lib/netcoreapp3.1/Yoti.Auth.dll": {} + }, + "runtime": { + "lib/netcoreapp3.1/Yoti.Auth.dll": {} + } + } + } + }, + "libraries": { + "dotenv.net/2.1.1": { + "sha512": "VWP/1PbyjRCLTnfXQNKDtd1yH/zLZKlpuehq2F564XjUXvPeFLJ2YCCY7+2KLkU5bEgumFzirs6Y8gkbRm7w2Q==", + "type": "package", + "path": "dotenv.net/2.1.1", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "dotenv.net.2.1.1.nupkg.sha512", + "dotenv.net.nuspec", + "lib/netstandard2.0/dotenv.net.dll" + ] + }, + "DotNetEnv/1.4.0": { + "sha512": "ye3WhbO/bHrgl6C6aE8VKKDewdRxEv1tSSnNMbYE1TxROegwkd1KXo/JjpBcC6nR/FbTpSBPZ2RzjxIimgVlSA==", + "type": "package", + "path": "dotnetenv/1.4.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE", + "dotnetenv.1.4.0.nupkg.sha512", + "dotnetenv.nuspec", + "lib/netstandard1.3/DotNetEnv.dll" + ] + }, + "Google.Protobuf/3.12.3": { + "sha512": "k+Dj0bdOy6/xhIZOthvDS2Ux9y5q6dWw+36n3SBVHYl520+tvcqLOpx6YsgQVy12cuzZbF+r73RZ0fxmpJ8c6A==", + "type": "package", + "path": "google.protobuf/3.12.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "google.protobuf.3.12.3.nupkg.sha512", + "google.protobuf.nuspec", + "lib/net45/Google.Protobuf.dll", + "lib/net45/Google.Protobuf.pdb", + "lib/net45/Google.Protobuf.xml", + "lib/netstandard1.0/Google.Protobuf.dll", + "lib/netstandard1.0/Google.Protobuf.pdb", + "lib/netstandard1.0/Google.Protobuf.xml", + "lib/netstandard2.0/Google.Protobuf.dll", + "lib/netstandard2.0/Google.Protobuf.pdb", + "lib/netstandard2.0/Google.Protobuf.xml" + ] + }, + "JsonSubTypes/1.7.0": { + "sha512": "RY23k6N/BvZ7VSsvgeFihfH42e+q2Lgxd+xydBqu2K6ya4rHd3snZJ2SLk7Xk7pMkl8Pn6lH2PD8uBcrTN0Qhg==", + "type": "package", + "path": "jsonsubtypes/1.7.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE", + "jsonsubtypes.1.7.0.nupkg.sha512", + "jsonsubtypes.nuspec", + "lib/net35/JsonSubTypes.dll", + "lib/net40/JsonSubTypes.dll", + "lib/net45/JsonSubTypes.dll", + "lib/net46/JsonSubTypes.dll", + "lib/net47/JsonSubTypes.dll", + "lib/netstandard1.3/JsonSubTypes.dll", + "lib/netstandard1.4/JsonSubTypes.dll", + "lib/netstandard1.5/JsonSubTypes.dll", + "lib/netstandard1.6/JsonSubTypes.dll", + "lib/netstandard2.0/JsonSubTypes.dll" + ] + }, + "Microsoft.AspNetCore.Hosting/2.2.7": { + "sha512": "O0ZBE53Fa9bVGXykDzvgMFW0Pe1QyPPFg1pazN8l3RUFSWBsDJ9/iD1LHXgADA8+ZD3R/1zkvraPa9SZdievxQ==", + "type": "package", + "path": "microsoft.aspnetcore.hosting/2.2.7", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.xml", + "microsoft.aspnetcore.hosting.2.2.7.nupkg.sha512", + "microsoft.aspnetcore.hosting.nuspec" + ] + }, + "Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": { + "sha512": "ubycklv+ZY7Kutdwuy1W4upWcZ6VFR8WUXU7l7B2+mvbDBBPAcfpi+E+Y5GFe+Q157YfA3C49D2GCjAZc7Mobw==", + "type": "package", + "path": "microsoft.aspnetcore.hosting.abstractions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.xml", + "microsoft.aspnetcore.hosting.abstractions.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.hosting.abstractions.nuspec" + ] + }, + "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { + "sha512": "1PMijw8RMtuQF60SsD/JlKtVfvh4NORAhF4wjysdABhlhTrYmtgssqyncR0Stq5vqtjplZcj6kbT4LRTglt9IQ==", + "type": "package", + "path": "microsoft.aspnetcore.hosting.server.abstractions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.xml", + "microsoft.aspnetcore.hosting.server.abstractions.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.hosting.server.abstractions.nuspec" + ] + }, + "Microsoft.AspNetCore.Http/2.2.0": { + "sha512": "YogBSMotWPAS/X5967pZ+yyWPQkThxhmzAwyCHCSSldzYBkW5W5d6oPfBaPqQOnSHYTpSOSOkpZoAce0vwb6+A==", + "type": "package", + "path": "microsoft.aspnetcore.http/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.xml", + "microsoft.aspnetcore.http.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.http.nuspec" + ] + }, + "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { + "sha512": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "type": "package", + "path": "microsoft.aspnetcore.http.abstractions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.xml", + "microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.http.abstractions.nuspec" + ] + }, + "Microsoft.AspNetCore.Http.Extensions/2.2.0": { + "sha512": "2DgZ9rWrJtuR7RYiew01nGRzuQBDaGHGmK56Rk54vsLLsCdzuFUPqbDTJCS1qJQWTbmbIQ9wGIOjpxA1t0l7/w==", + "type": "package", + "path": "microsoft.aspnetcore.http.extensions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.xml", + "microsoft.aspnetcore.http.extensions.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.http.extensions.nuspec" + ] + }, + "Microsoft.AspNetCore.Http.Features/2.2.0": { + "sha512": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "type": "package", + "path": "microsoft.aspnetcore.http.features/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.xml", + "microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.http.features.nuspec" + ] + }, + "Microsoft.AspNetCore.WebUtilities/2.2.0": { + "sha512": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", + "type": "package", + "path": "microsoft.aspnetcore.webutilities/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll", + "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.xml", + "microsoft.aspnetcore.webutilities.2.2.0.nupkg.sha512", + "microsoft.aspnetcore.webutilities.nuspec" + ] + }, + "Microsoft.Extensions.Configuration/3.1.8": { + "sha512": "xWvtu/ra8xDOy62ZXzQj1ElmmH3GpZBSKvw4LbfNXKCy+PaziS5Uh0gQ47D4H4w3u+PJfhNWCCGCp9ORNEzkRw==", + "type": "package", + "path": "microsoft.extensions.configuration/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.xml", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.xml", + "microsoft.extensions.configuration.3.1.8.nupkg.sha512", + "microsoft.extensions.configuration.nuspec" + ] + }, + "Microsoft.Extensions.Configuration.Abstractions/3.1.8": { + "sha512": "0qbNyxGpuNP/fuQ3FLHesm1Vn/83qYcAgVsi1UQCQN1peY4YH1uiizOh4xbYkQyxiVMD/c/zhiYYv94G0DXSSA==", + "type": "package", + "path": "microsoft.extensions.configuration.abstractions/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.xml", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.xml", + "microsoft.extensions.configuration.abstractions.3.1.8.nupkg.sha512", + "microsoft.extensions.configuration.abstractions.nuspec" + ] + }, + "Microsoft.Extensions.Configuration.Binder/3.1.8": { + "sha512": "l/oqIWRM4YF62mlCOrIKGUOCemsaID/lngK2SZEtpYI8LrktpjPd4QzvENWj5GebbLbqOtsFhF6Ko6dgzmUnBw==", + "type": "package", + "path": "microsoft.extensions.configuration.binder/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.xml", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.xml", + "microsoft.extensions.configuration.binder.3.1.8.nupkg.sha512", + "microsoft.extensions.configuration.binder.nuspec" + ] + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables/2.2.4": { + "sha512": "Os7uRhp9xwawY5w2tgXw/86YrmAJZl6aHiQVdS9boWybTWPkJOvXXrQ3AGwuldN1W/r+cfnwRe2ePGeFO4zlzg==", + "type": "package", + "path": "microsoft.extensions.configuration.environmentvariables/2.2.4", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.xml", + "microsoft.extensions.configuration.environmentvariables.2.2.4.nupkg.sha512", + "microsoft.extensions.configuration.environmentvariables.nuspec" + ] + }, + "Microsoft.Extensions.Configuration.FileExtensions/2.2.0": { + "sha512": "H1qCpWBC8Ed4tguTR/qYkbb3F6DI5Su3t8xyFo3/5MzAd8PwPpHzgX8X04KbBxKmk173Pb64x7xMHarczVFQUA==", + "type": "package", + "path": "microsoft.extensions.configuration.fileextensions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll", + "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.xml", + "microsoft.extensions.configuration.fileextensions.2.2.0.nupkg.sha512", + "microsoft.extensions.configuration.fileextensions.nuspec" + ] + }, + "Microsoft.Extensions.DependencyInjection/3.1.8": { + "sha512": "tUpYcVxFqwh8wVD8O+6A8gJnVtl6L4N1Vd9bLJgQSJ0gjBTUQ/eKwJn0LglkkaDU7GAxODDv4eexgZn3QSE0NQ==", + "type": "package", + "path": "microsoft.extensions.dependencyinjection/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/net461/Microsoft.Extensions.DependencyInjection.dll", + "lib/net461/Microsoft.Extensions.DependencyInjection.xml", + "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.xml", + "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.dll", + "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.xml", + "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.dll", + "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.xml", + "microsoft.extensions.dependencyinjection.3.1.8.nupkg.sha512", + "microsoft.extensions.dependencyinjection.nuspec" + ] + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/3.1.8": { + "sha512": "YP0kEBkSLTVl3znqZEux+xyJpz5iVNwFZf0OPS7nupdKbojSlO7Fa+JuQjLYpWfpAshaMcznu27tjWzfXRJnOA==", + "type": "package", + "path": "microsoft.extensions.dependencyinjection.abstractions/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", + "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", + "microsoft.extensions.dependencyinjection.abstractions.3.1.8.nupkg.sha512", + "microsoft.extensions.dependencyinjection.abstractions.nuspec" + ] + }, + "Microsoft.Extensions.FileProviders.Abstractions/2.2.0": { + "sha512": "EcnaSsPTqx2MGnHrmWOD0ugbuuqVT8iICqSqPzi45V5/MA1LjUNb0kwgcxBGqizV1R+WeBK7/Gw25Jzkyk9bIw==", + "type": "package", + "path": "microsoft.extensions.fileproviders.abstractions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll", + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.xml", + "microsoft.extensions.fileproviders.abstractions.2.2.0.nupkg.sha512", + "microsoft.extensions.fileproviders.abstractions.nuspec" + ] + }, + "Microsoft.Extensions.FileProviders.Physical/2.2.0": { + "sha512": "tbDHZnBJkjYd9NjlRZ9ondDiv1Te3KYCTW2RWpR1B0e1Z8+EnFRo7qNnHkkSCixLdlPZzhjlX24d/PixQ7w2dA==", + "type": "package", + "path": "microsoft.extensions.fileproviders.physical/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll", + "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.xml", + "microsoft.extensions.fileproviders.physical.2.2.0.nupkg.sha512", + "microsoft.extensions.fileproviders.physical.nuspec" + ] + }, + "Microsoft.Extensions.FileSystemGlobbing/2.2.0": { + "sha512": "ZSsHZp3PyW6vk37tDEdypjgGlNtpJ0EixBMOfUod2Thx7GtwfFSAQXUQx8a8BN8vfWKGGMbp7jPWdoHx/At4wQ==", + "type": "package", + "path": "microsoft.extensions.filesystemglobbing/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll", + "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.xml", + "microsoft.extensions.filesystemglobbing.2.2.0.nupkg.sha512", + "microsoft.extensions.filesystemglobbing.nuspec" + ] + }, + "Microsoft.Extensions.Hosting.Abstractions/2.2.0": { + "sha512": "+k4AEn68HOJat5gj1TWa6X28WlirNQO9sPIIeQbia+91n03esEtMSSoekSTpMjUzjqtJWQN3McVx0GvSPFHF/Q==", + "type": "package", + "path": "microsoft.extensions.hosting.abstractions/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll", + "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.xml", + "microsoft.extensions.hosting.abstractions.2.2.0.nupkg.sha512", + "microsoft.extensions.hosting.abstractions.nuspec" + ] + }, + "Microsoft.Extensions.Logging/3.1.8": { + "sha512": "Bch88WGwrgJUabSOiTbPgne/jkCcWTyP97db8GWzQH9RcGi6TThiRm8ggsD+OXBW2UBwAYx1Zb1ns1elsMiomQ==", + "type": "package", + "path": "microsoft.extensions.logging/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.xml", + "lib/netstandard2.0/Microsoft.Extensions.Logging.dll", + "lib/netstandard2.0/Microsoft.Extensions.Logging.xml", + "microsoft.extensions.logging.3.1.8.nupkg.sha512", + "microsoft.extensions.logging.nuspec" + ] + }, + "Microsoft.Extensions.Logging.Abstractions/3.1.8": { + "sha512": "LxQPR/KE4P9nx304VcFipWPcW8ZOZOGHuiYlG0ncAQJItogDzR9nyYUNvziLObx2MfX2Z9iCTdAqEtoImaQOYg==", + "type": "package", + "path": "microsoft.extensions.logging.abstractions/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll", + "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.xml", + "microsoft.extensions.logging.abstractions.3.1.8.nupkg.sha512", + "microsoft.extensions.logging.abstractions.nuspec" + ] + }, + "Microsoft.Extensions.Logging.Configuration/3.1.8": { + "sha512": "tfvQYDDwc3ni8VTXQavINMHRUIsM8fQ8gBQTs98mt1QFE3YIVm8XzhqPZMBBlLnXTESymb8HctM2fMnU8qC8Rg==", + "type": "package", + "path": "microsoft.extensions.logging.configuration/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.xml", + "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll", + "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.xml", + "microsoft.extensions.logging.configuration.3.1.8.nupkg.sha512", + "microsoft.extensions.logging.configuration.nuspec" + ] + }, + "Microsoft.Extensions.Logging.Console/3.1.8": { + "sha512": "7JCZuqty78ZDwoXOGXGUXMC44rpocfeaa/48ONY7neKhCh2Oml/faW3PANPCdwy6M3TicmU03kIOhyrw3dQ2Eg==", + "type": "package", + "path": "microsoft.extensions.logging.console/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.xml", + "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.dll", + "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.xml", + "microsoft.extensions.logging.console.3.1.8.nupkg.sha512", + "microsoft.extensions.logging.console.nuspec" + ] + }, + "Microsoft.Extensions.ObjectPool/2.2.0": { + "sha512": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==", + "type": "package", + "path": "microsoft.extensions.objectpool/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll", + "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.xml", + "microsoft.extensions.objectpool.2.2.0.nupkg.sha512", + "microsoft.extensions.objectpool.nuspec" + ] + }, + "Microsoft.Extensions.Options/3.1.8": { + "sha512": "mpkwjNg5sr1XHEJwVS8G1w6dsh5/72vQOOe4aqhg012j93m8OOmfyIBwoQN4SE0KRRS+fatdW3qqUrHbRwlWOA==", + "type": "package", + "path": "microsoft.extensions.options/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Options.xml", + "lib/netstandard2.0/Microsoft.Extensions.Options.dll", + "lib/netstandard2.0/Microsoft.Extensions.Options.xml", + "microsoft.extensions.options.3.1.8.nupkg.sha512", + "microsoft.extensions.options.nuspec" + ] + }, + "Microsoft.Extensions.Options.ConfigurationExtensions/3.1.8": { + "sha512": "/q7OhcsgDq6cPqg03nv55QqLt8o/OAvrVkd/w6h0YNasZ4C/Lxpx6I0DsnIH0MB5ORnqCyhmeyv1hFqOeehJng==", + "type": "package", + "path": "microsoft.extensions.options.configurationextensions/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.xml", + "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll", + "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.xml", + "microsoft.extensions.options.configurationextensions.3.1.8.nupkg.sha512", + "microsoft.extensions.options.configurationextensions.nuspec" + ] + }, + "Microsoft.Extensions.Primitives/3.1.8": { + "sha512": "XcIoXQhT0kwnEhOKv/LmpWR6yF6QWmBTy9Fcsz4aHuCOgTJ7Zd23ELtUA4BfwlYoFlSedavS+vURz9tNekd44g==", + "type": "package", + "path": "microsoft.extensions.primitives/3.1.8", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Icon.png", + "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.dll", + "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.xml", + "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll", + "lib/netstandard2.0/Microsoft.Extensions.Primitives.xml", + "microsoft.extensions.primitives.3.1.8.nupkg.sha512", + "microsoft.extensions.primitives.nuspec" + ] + }, + "Microsoft.Net.Http.Headers/2.2.0": { + "sha512": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", + "type": "package", + "path": "microsoft.net.http.headers/2.2.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll", + "lib/netstandard2.0/Microsoft.Net.Http.Headers.xml", + "microsoft.net.http.headers.2.2.0.nupkg.sha512", + "microsoft.net.http.headers.nuspec" + ] + }, + "Microsoft.NETCore.Platforms/1.1.1": { + "sha512": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==", + "type": "package", + "path": "microsoft.netcore.platforms/1.1.1", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "microsoft.netcore.platforms.1.1.1.nupkg.sha512", + "microsoft.netcore.platforms.nuspec", + "runtime.json" + ] + }, + "Microsoft.NETCore.Targets/1.1.0": { + "sha512": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==", + "type": "package", + "path": "microsoft.netcore.targets/1.1.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "microsoft.netcore.targets.1.1.0.nupkg.sha512", + "microsoft.netcore.targets.nuspec", + "runtime.json" + ] + }, + "Microsoft.Win32.Primitives/4.3.0": { + "sha512": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "type": "package", + "path": "microsoft.win32.primitives/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/Microsoft.Win32.Primitives.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "microsoft.win32.primitives.4.3.0.nupkg.sha512", + "microsoft.win32.primitives.nuspec", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/Microsoft.Win32.Primitives.dll", + "ref/netstandard1.3/Microsoft.Win32.Primitives.dll", + "ref/netstandard1.3/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/de/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/es/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/fr/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/it/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/ja/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/ko/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/ru/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/zh-hans/Microsoft.Win32.Primitives.xml", + "ref/netstandard1.3/zh-hant/Microsoft.Win32.Primitives.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._" + ] + }, + "NETStandard.Library/1.6.1": { + "sha512": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "type": "package", + "path": "netstandard.library/1.6.1", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "netstandard.library.1.6.1.nupkg.sha512", + "netstandard.library.nuspec" + ] + }, + "Newtonsoft.Json/12.0.3": { + "sha512": "6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==", + "type": "package", + "path": "newtonsoft.json/12.0.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.md", + "lib/net20/Newtonsoft.Json.dll", + "lib/net20/Newtonsoft.Json.xml", + "lib/net35/Newtonsoft.Json.dll", + "lib/net35/Newtonsoft.Json.xml", + "lib/net40/Newtonsoft.Json.dll", + "lib/net40/Newtonsoft.Json.xml", + "lib/net45/Newtonsoft.Json.dll", + "lib/net45/Newtonsoft.Json.xml", + "lib/netstandard1.0/Newtonsoft.Json.dll", + "lib/netstandard1.0/Newtonsoft.Json.xml", + "lib/netstandard1.3/Newtonsoft.Json.dll", + "lib/netstandard1.3/Newtonsoft.Json.xml", + "lib/netstandard2.0/Newtonsoft.Json.dll", + "lib/netstandard2.0/Newtonsoft.Json.xml", + "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll", + "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml", + "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll", + "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml", + "newtonsoft.json.12.0.3.nupkg.sha512", + "newtonsoft.json.nuspec", + "packageIcon.png" + ] + }, + "NLog/4.7.2": { + "sha512": "tfZNTOYr0TwvJNrXsJsVdZPSgIpYN1rXHnAZAadeaIbz+v18agNCCxgZXnXQqNKNizkogJ68zYTNKavyQFWwrg==", + "type": "package", + "path": "nlog/4.7.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/monoandroid44/NLog.dll", + "lib/monoandroid44/NLog.xml", + "lib/net35/NLog.dll", + "lib/net35/NLog.xml", + "lib/net40-client/NLog.dll", + "lib/net40-client/NLog.xml", + "lib/net45/NLog.dll", + "lib/net45/NLog.xml", + "lib/netstandard1.3/NLog.dll", + "lib/netstandard1.3/NLog.xml", + "lib/netstandard1.5/NLog.dll", + "lib/netstandard1.5/NLog.xml", + "lib/netstandard2.0/NLog.dll", + "lib/netstandard2.0/NLog.xml", + "lib/sl4/NLog.dll", + "lib/sl4/NLog.xml", + "lib/sl5/NLog.dll", + "lib/sl5/NLog.xml", + "lib/wp8/NLog.dll", + "lib/wp8/NLog.xml", + "lib/xamarinios10/NLog.dll", + "lib/xamarinios10/NLog.xml", + "nlog.4.7.2.nupkg.sha512", + "nlog.nuspec" + ] + }, + "Portable.BouncyCastle/1.8.5": { + "sha512": "EaCgmntbH1sOzemRTqyXSqYjB6pLH7VCYHhhDYZ59guHSD5qPwhIYa7kfy0QUlmTRt9IXhaXdFhNuBUArp70Ng==", + "type": "package", + "path": "portable.bouncycastle/1.8.5", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/net40/BouncyCastle.Crypto.dll", + "lib/net40/BouncyCastle.Crypto.xml", + "lib/netstandard1.0/BouncyCastle.Crypto.dll", + "lib/netstandard1.0/BouncyCastle.Crypto.xml", + "lib/netstandard1.3/BouncyCastle.Crypto.dll", + "lib/netstandard1.3/BouncyCastle.Crypto.xml", + "lib/netstandard2.0/BouncyCastle.Crypto.dll", + "lib/netstandard2.0/BouncyCastle.Crypto.xml", + "portable.bouncycastle.1.8.5.nupkg.sha512", + "portable.bouncycastle.nuspec" + ] + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==", + "type": "package", + "path": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==", + "type": "package", + "path": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==", + "type": "package", + "path": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.native.System/4.3.0": { + "sha512": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "type": "package", + "path": "runtime.native.system/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "runtime.native.system.4.3.0.nupkg.sha512", + "runtime.native.system.nuspec" + ] + }, + "runtime.native.System.IO.Compression/4.3.0": { + "sha512": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "type": "package", + "path": "runtime.native.system.io.compression/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "runtime.native.system.io.compression.4.3.0.nupkg.sha512", + "runtime.native.system.io.compression.nuspec" + ] + }, + "runtime.native.System.Net.Http/4.3.0": { + "sha512": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "type": "package", + "path": "runtime.native.system.net.http/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "runtime.native.system.net.http.4.3.0.nupkg.sha512", + "runtime.native.system.net.http.nuspec" + ] + }, + "runtime.native.System.Security.Cryptography.Apple/4.3.0": { + "sha512": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "type": "package", + "path": "runtime.native.system.security.cryptography.apple/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", + "runtime.native.system.security.cryptography.apple.nuspec" + ] + }, + "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", + "type": "package", + "path": "runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/_._", + "runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.native.system.security.cryptography.openssl.nuspec" + ] + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==", + "type": "package", + "path": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==", + "type": "package", + "path": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { + "sha512": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==", + "type": "package", + "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", + "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.nuspec", + "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib" + ] + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==", + "type": "package", + "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib" + ] + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==", + "type": "package", + "path": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==", + "type": "package", + "path": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==", + "type": "package", + "path": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { + "sha512": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==", + "type": "package", + "path": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", + "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so" + ] + }, + "System.AppContext/4.3.0": { + "sha512": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "type": "package", + "path": "system.appcontext/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.AppContext.dll", + "lib/net463/System.AppContext.dll", + "lib/netcore50/System.AppContext.dll", + "lib/netstandard1.6/System.AppContext.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.AppContext.dll", + "ref/net463/System.AppContext.dll", + "ref/netstandard/_._", + "ref/netstandard1.3/System.AppContext.dll", + "ref/netstandard1.3/System.AppContext.xml", + "ref/netstandard1.3/de/System.AppContext.xml", + "ref/netstandard1.3/es/System.AppContext.xml", + "ref/netstandard1.3/fr/System.AppContext.xml", + "ref/netstandard1.3/it/System.AppContext.xml", + "ref/netstandard1.3/ja/System.AppContext.xml", + "ref/netstandard1.3/ko/System.AppContext.xml", + "ref/netstandard1.3/ru/System.AppContext.xml", + "ref/netstandard1.3/zh-hans/System.AppContext.xml", + "ref/netstandard1.3/zh-hant/System.AppContext.xml", + "ref/netstandard1.6/System.AppContext.dll", + "ref/netstandard1.6/System.AppContext.xml", + "ref/netstandard1.6/de/System.AppContext.xml", + "ref/netstandard1.6/es/System.AppContext.xml", + "ref/netstandard1.6/fr/System.AppContext.xml", + "ref/netstandard1.6/it/System.AppContext.xml", + "ref/netstandard1.6/ja/System.AppContext.xml", + "ref/netstandard1.6/ko/System.AppContext.xml", + "ref/netstandard1.6/ru/System.AppContext.xml", + "ref/netstandard1.6/zh-hans/System.AppContext.xml", + "ref/netstandard1.6/zh-hant/System.AppContext.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/System.AppContext.dll", + "system.appcontext.4.3.0.nupkg.sha512", + "system.appcontext.nuspec" + ] + }, + "System.Buffers/4.5.0": { + "sha512": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==", + "type": "package", + "path": "system.buffers/4.5.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netcoreapp2.0/_._", + "lib/netstandard1.1/System.Buffers.dll", + "lib/netstandard1.1/System.Buffers.xml", + "lib/netstandard2.0/System.Buffers.dll", + "lib/netstandard2.0/System.Buffers.xml", + "lib/uap10.0.16299/_._", + "ref/net45/System.Buffers.dll", + "ref/net45/System.Buffers.xml", + "ref/netcoreapp2.0/_._", + "ref/netstandard1.1/System.Buffers.dll", + "ref/netstandard1.1/System.Buffers.xml", + "ref/netstandard2.0/System.Buffers.dll", + "ref/netstandard2.0/System.Buffers.xml", + "ref/uap10.0.16299/_._", + "system.buffers.4.5.0.nupkg.sha512", + "system.buffers.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Collections/4.3.0": { + "sha512": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "type": "package", + "path": "system.collections/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Collections.dll", + "ref/netcore50/System.Collections.xml", + "ref/netcore50/de/System.Collections.xml", + "ref/netcore50/es/System.Collections.xml", + "ref/netcore50/fr/System.Collections.xml", + "ref/netcore50/it/System.Collections.xml", + "ref/netcore50/ja/System.Collections.xml", + "ref/netcore50/ko/System.Collections.xml", + "ref/netcore50/ru/System.Collections.xml", + "ref/netcore50/zh-hans/System.Collections.xml", + "ref/netcore50/zh-hant/System.Collections.xml", + "ref/netstandard1.0/System.Collections.dll", + "ref/netstandard1.0/System.Collections.xml", + "ref/netstandard1.0/de/System.Collections.xml", + "ref/netstandard1.0/es/System.Collections.xml", + "ref/netstandard1.0/fr/System.Collections.xml", + "ref/netstandard1.0/it/System.Collections.xml", + "ref/netstandard1.0/ja/System.Collections.xml", + "ref/netstandard1.0/ko/System.Collections.xml", + "ref/netstandard1.0/ru/System.Collections.xml", + "ref/netstandard1.0/zh-hans/System.Collections.xml", + "ref/netstandard1.0/zh-hant/System.Collections.xml", + "ref/netstandard1.3/System.Collections.dll", + "ref/netstandard1.3/System.Collections.xml", + "ref/netstandard1.3/de/System.Collections.xml", + "ref/netstandard1.3/es/System.Collections.xml", + "ref/netstandard1.3/fr/System.Collections.xml", + "ref/netstandard1.3/it/System.Collections.xml", + "ref/netstandard1.3/ja/System.Collections.xml", + "ref/netstandard1.3/ko/System.Collections.xml", + "ref/netstandard1.3/ru/System.Collections.xml", + "ref/netstandard1.3/zh-hans/System.Collections.xml", + "ref/netstandard1.3/zh-hant/System.Collections.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.collections.4.3.0.nupkg.sha512", + "system.collections.nuspec" + ] + }, + "System.Collections.Concurrent/4.3.0": { + "sha512": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "type": "package", + "path": "system.collections.concurrent/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Collections.Concurrent.dll", + "lib/netstandard1.3/System.Collections.Concurrent.dll", + "lib/portable-net45+win8+wpa81/_._", + "lib/win8/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Collections.Concurrent.dll", + "ref/netcore50/System.Collections.Concurrent.xml", + "ref/netcore50/de/System.Collections.Concurrent.xml", + "ref/netcore50/es/System.Collections.Concurrent.xml", + "ref/netcore50/fr/System.Collections.Concurrent.xml", + "ref/netcore50/it/System.Collections.Concurrent.xml", + "ref/netcore50/ja/System.Collections.Concurrent.xml", + "ref/netcore50/ko/System.Collections.Concurrent.xml", + "ref/netcore50/ru/System.Collections.Concurrent.xml", + "ref/netcore50/zh-hans/System.Collections.Concurrent.xml", + "ref/netcore50/zh-hant/System.Collections.Concurrent.xml", + "ref/netstandard1.1/System.Collections.Concurrent.dll", + "ref/netstandard1.1/System.Collections.Concurrent.xml", + "ref/netstandard1.1/de/System.Collections.Concurrent.xml", + "ref/netstandard1.1/es/System.Collections.Concurrent.xml", + "ref/netstandard1.1/fr/System.Collections.Concurrent.xml", + "ref/netstandard1.1/it/System.Collections.Concurrent.xml", + "ref/netstandard1.1/ja/System.Collections.Concurrent.xml", + "ref/netstandard1.1/ko/System.Collections.Concurrent.xml", + "ref/netstandard1.1/ru/System.Collections.Concurrent.xml", + "ref/netstandard1.1/zh-hans/System.Collections.Concurrent.xml", + "ref/netstandard1.1/zh-hant/System.Collections.Concurrent.xml", + "ref/netstandard1.3/System.Collections.Concurrent.dll", + "ref/netstandard1.3/System.Collections.Concurrent.xml", + "ref/netstandard1.3/de/System.Collections.Concurrent.xml", + "ref/netstandard1.3/es/System.Collections.Concurrent.xml", + "ref/netstandard1.3/fr/System.Collections.Concurrent.xml", + "ref/netstandard1.3/it/System.Collections.Concurrent.xml", + "ref/netstandard1.3/ja/System.Collections.Concurrent.xml", + "ref/netstandard1.3/ko/System.Collections.Concurrent.xml", + "ref/netstandard1.3/ru/System.Collections.Concurrent.xml", + "ref/netstandard1.3/zh-hans/System.Collections.Concurrent.xml", + "ref/netstandard1.3/zh-hant/System.Collections.Concurrent.xml", + "ref/portable-net45+win8+wpa81/_._", + "ref/win8/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.collections.concurrent.4.3.0.nupkg.sha512", + "system.collections.concurrent.nuspec" + ] + }, + "System.Console/4.3.0": { + "sha512": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "type": "package", + "path": "system.console/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Console.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Console.dll", + "ref/netstandard1.3/System.Console.dll", + "ref/netstandard1.3/System.Console.xml", + "ref/netstandard1.3/de/System.Console.xml", + "ref/netstandard1.3/es/System.Console.xml", + "ref/netstandard1.3/fr/System.Console.xml", + "ref/netstandard1.3/it/System.Console.xml", + "ref/netstandard1.3/ja/System.Console.xml", + "ref/netstandard1.3/ko/System.Console.xml", + "ref/netstandard1.3/ru/System.Console.xml", + "ref/netstandard1.3/zh-hans/System.Console.xml", + "ref/netstandard1.3/zh-hant/System.Console.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.console.4.3.0.nupkg.sha512", + "system.console.nuspec" + ] + }, + "System.Diagnostics.Debug/4.3.0": { + "sha512": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "type": "package", + "path": "system.diagnostics.debug/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Diagnostics.Debug.dll", + "ref/netcore50/System.Diagnostics.Debug.xml", + "ref/netcore50/de/System.Diagnostics.Debug.xml", + "ref/netcore50/es/System.Diagnostics.Debug.xml", + "ref/netcore50/fr/System.Diagnostics.Debug.xml", + "ref/netcore50/it/System.Diagnostics.Debug.xml", + "ref/netcore50/ja/System.Diagnostics.Debug.xml", + "ref/netcore50/ko/System.Diagnostics.Debug.xml", + "ref/netcore50/ru/System.Diagnostics.Debug.xml", + "ref/netcore50/zh-hans/System.Diagnostics.Debug.xml", + "ref/netcore50/zh-hant/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/System.Diagnostics.Debug.dll", + "ref/netstandard1.0/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/de/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/es/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/fr/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/it/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/ja/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/ko/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/ru/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/zh-hans/System.Diagnostics.Debug.xml", + "ref/netstandard1.0/zh-hant/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/System.Diagnostics.Debug.dll", + "ref/netstandard1.3/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/de/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/es/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/fr/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/it/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/ja/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/ko/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/ru/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/zh-hans/System.Diagnostics.Debug.xml", + "ref/netstandard1.3/zh-hant/System.Diagnostics.Debug.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.diagnostics.debug.4.3.0.nupkg.sha512", + "system.diagnostics.debug.nuspec" + ] + }, + "System.Diagnostics.DiagnosticSource/4.5.1": { + "sha512": "zCno/m44ymWhgLFh7tELDG9587q0l/EynPM0m4KgLaWQbz/TEKvNRX2YT5ip2qXW/uayifQ2ZqbnErsKJ4lYrQ==", + "type": "package", + "path": "system.diagnostics.diagnosticsource/4.5.1", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/net45/System.Diagnostics.DiagnosticSource.dll", + "lib/net45/System.Diagnostics.DiagnosticSource.xml", + "lib/net46/System.Diagnostics.DiagnosticSource.dll", + "lib/net46/System.Diagnostics.DiagnosticSource.xml", + "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.dll", + "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.xml", + "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll", + "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.xml", + "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.dll", + "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.xml", + "system.diagnostics.diagnosticsource.4.5.1.nupkg.sha512", + "system.diagnostics.diagnosticsource.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Diagnostics.Tools/4.3.0": { + "sha512": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "type": "package", + "path": "system.diagnostics.tools/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Diagnostics.Tools.dll", + "ref/netcore50/System.Diagnostics.Tools.xml", + "ref/netcore50/de/System.Diagnostics.Tools.xml", + "ref/netcore50/es/System.Diagnostics.Tools.xml", + "ref/netcore50/fr/System.Diagnostics.Tools.xml", + "ref/netcore50/it/System.Diagnostics.Tools.xml", + "ref/netcore50/ja/System.Diagnostics.Tools.xml", + "ref/netcore50/ko/System.Diagnostics.Tools.xml", + "ref/netcore50/ru/System.Diagnostics.Tools.xml", + "ref/netcore50/zh-hans/System.Diagnostics.Tools.xml", + "ref/netcore50/zh-hant/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/System.Diagnostics.Tools.dll", + "ref/netstandard1.0/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/de/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/es/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/fr/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/it/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/ja/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/ko/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/ru/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/zh-hans/System.Diagnostics.Tools.xml", + "ref/netstandard1.0/zh-hant/System.Diagnostics.Tools.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.diagnostics.tools.4.3.0.nupkg.sha512", + "system.diagnostics.tools.nuspec" + ] + }, + "System.Diagnostics.Tracing/4.3.0": { + "sha512": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "type": "package", + "path": "system.diagnostics.tracing/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net462/System.Diagnostics.Tracing.dll", + "lib/portable-net45+win8+wpa81/_._", + "lib/win8/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net462/System.Diagnostics.Tracing.dll", + "ref/netcore50/System.Diagnostics.Tracing.dll", + "ref/netcore50/System.Diagnostics.Tracing.xml", + "ref/netcore50/de/System.Diagnostics.Tracing.xml", + "ref/netcore50/es/System.Diagnostics.Tracing.xml", + "ref/netcore50/fr/System.Diagnostics.Tracing.xml", + "ref/netcore50/it/System.Diagnostics.Tracing.xml", + "ref/netcore50/ja/System.Diagnostics.Tracing.xml", + "ref/netcore50/ko/System.Diagnostics.Tracing.xml", + "ref/netcore50/ru/System.Diagnostics.Tracing.xml", + "ref/netcore50/zh-hans/System.Diagnostics.Tracing.xml", + "ref/netcore50/zh-hant/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/System.Diagnostics.Tracing.dll", + "ref/netstandard1.1/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/de/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/es/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/fr/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/it/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/ja/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/ko/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/ru/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/zh-hans/System.Diagnostics.Tracing.xml", + "ref/netstandard1.1/zh-hant/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/System.Diagnostics.Tracing.dll", + "ref/netstandard1.2/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/de/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/es/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/fr/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/it/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/ja/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/ko/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/ru/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/zh-hans/System.Diagnostics.Tracing.xml", + "ref/netstandard1.2/zh-hant/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/System.Diagnostics.Tracing.dll", + "ref/netstandard1.3/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/de/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/es/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/fr/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/it/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/ja/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/ko/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/ru/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/zh-hans/System.Diagnostics.Tracing.xml", + "ref/netstandard1.3/zh-hant/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/System.Diagnostics.Tracing.dll", + "ref/netstandard1.5/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/de/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/es/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/fr/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/it/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/ja/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/ko/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/ru/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/zh-hans/System.Diagnostics.Tracing.xml", + "ref/netstandard1.5/zh-hant/System.Diagnostics.Tracing.xml", + "ref/portable-net45+win8+wpa81/_._", + "ref/win8/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.diagnostics.tracing.4.3.0.nupkg.sha512", + "system.diagnostics.tracing.nuspec" + ] + }, + "System.Globalization/4.3.0": { + "sha512": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "type": "package", + "path": "system.globalization/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Globalization.dll", + "ref/netcore50/System.Globalization.xml", + "ref/netcore50/de/System.Globalization.xml", + "ref/netcore50/es/System.Globalization.xml", + "ref/netcore50/fr/System.Globalization.xml", + "ref/netcore50/it/System.Globalization.xml", + "ref/netcore50/ja/System.Globalization.xml", + "ref/netcore50/ko/System.Globalization.xml", + "ref/netcore50/ru/System.Globalization.xml", + "ref/netcore50/zh-hans/System.Globalization.xml", + "ref/netcore50/zh-hant/System.Globalization.xml", + "ref/netstandard1.0/System.Globalization.dll", + "ref/netstandard1.0/System.Globalization.xml", + "ref/netstandard1.0/de/System.Globalization.xml", + "ref/netstandard1.0/es/System.Globalization.xml", + "ref/netstandard1.0/fr/System.Globalization.xml", + "ref/netstandard1.0/it/System.Globalization.xml", + "ref/netstandard1.0/ja/System.Globalization.xml", + "ref/netstandard1.0/ko/System.Globalization.xml", + "ref/netstandard1.0/ru/System.Globalization.xml", + "ref/netstandard1.0/zh-hans/System.Globalization.xml", + "ref/netstandard1.0/zh-hant/System.Globalization.xml", + "ref/netstandard1.3/System.Globalization.dll", + "ref/netstandard1.3/System.Globalization.xml", + "ref/netstandard1.3/de/System.Globalization.xml", + "ref/netstandard1.3/es/System.Globalization.xml", + "ref/netstandard1.3/fr/System.Globalization.xml", + "ref/netstandard1.3/it/System.Globalization.xml", + "ref/netstandard1.3/ja/System.Globalization.xml", + "ref/netstandard1.3/ko/System.Globalization.xml", + "ref/netstandard1.3/ru/System.Globalization.xml", + "ref/netstandard1.3/zh-hans/System.Globalization.xml", + "ref/netstandard1.3/zh-hant/System.Globalization.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.globalization.4.3.0.nupkg.sha512", + "system.globalization.nuspec" + ] + }, + "System.Globalization.Calendars/4.3.0": { + "sha512": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "type": "package", + "path": "system.globalization.calendars/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Globalization.Calendars.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Globalization.Calendars.dll", + "ref/netstandard1.3/System.Globalization.Calendars.dll", + "ref/netstandard1.3/System.Globalization.Calendars.xml", + "ref/netstandard1.3/de/System.Globalization.Calendars.xml", + "ref/netstandard1.3/es/System.Globalization.Calendars.xml", + "ref/netstandard1.3/fr/System.Globalization.Calendars.xml", + "ref/netstandard1.3/it/System.Globalization.Calendars.xml", + "ref/netstandard1.3/ja/System.Globalization.Calendars.xml", + "ref/netstandard1.3/ko/System.Globalization.Calendars.xml", + "ref/netstandard1.3/ru/System.Globalization.Calendars.xml", + "ref/netstandard1.3/zh-hans/System.Globalization.Calendars.xml", + "ref/netstandard1.3/zh-hant/System.Globalization.Calendars.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.globalization.calendars.4.3.0.nupkg.sha512", + "system.globalization.calendars.nuspec" + ] + }, + "System.Globalization.Extensions/4.3.0": { + "sha512": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "type": "package", + "path": "system.globalization.extensions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Globalization.Extensions.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Globalization.Extensions.dll", + "ref/netstandard1.3/System.Globalization.Extensions.dll", + "ref/netstandard1.3/System.Globalization.Extensions.xml", + "ref/netstandard1.3/de/System.Globalization.Extensions.xml", + "ref/netstandard1.3/es/System.Globalization.Extensions.xml", + "ref/netstandard1.3/fr/System.Globalization.Extensions.xml", + "ref/netstandard1.3/it/System.Globalization.Extensions.xml", + "ref/netstandard1.3/ja/System.Globalization.Extensions.xml", + "ref/netstandard1.3/ko/System.Globalization.Extensions.xml", + "ref/netstandard1.3/ru/System.Globalization.Extensions.xml", + "ref/netstandard1.3/zh-hans/System.Globalization.Extensions.xml", + "ref/netstandard1.3/zh-hant/System.Globalization.Extensions.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll", + "runtimes/win/lib/net46/System.Globalization.Extensions.dll", + "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll", + "system.globalization.extensions.4.3.0.nupkg.sha512", + "system.globalization.extensions.nuspec" + ] + }, + "System.IO/4.3.0": { + "sha512": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "type": "package", + "path": "system.io/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net462/System.IO.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net462/System.IO.dll", + "ref/netcore50/System.IO.dll", + "ref/netcore50/System.IO.xml", + "ref/netcore50/de/System.IO.xml", + "ref/netcore50/es/System.IO.xml", + "ref/netcore50/fr/System.IO.xml", + "ref/netcore50/it/System.IO.xml", + "ref/netcore50/ja/System.IO.xml", + "ref/netcore50/ko/System.IO.xml", + "ref/netcore50/ru/System.IO.xml", + "ref/netcore50/zh-hans/System.IO.xml", + "ref/netcore50/zh-hant/System.IO.xml", + "ref/netstandard1.0/System.IO.dll", + "ref/netstandard1.0/System.IO.xml", + "ref/netstandard1.0/de/System.IO.xml", + "ref/netstandard1.0/es/System.IO.xml", + "ref/netstandard1.0/fr/System.IO.xml", + "ref/netstandard1.0/it/System.IO.xml", + "ref/netstandard1.0/ja/System.IO.xml", + "ref/netstandard1.0/ko/System.IO.xml", + "ref/netstandard1.0/ru/System.IO.xml", + "ref/netstandard1.0/zh-hans/System.IO.xml", + "ref/netstandard1.0/zh-hant/System.IO.xml", + "ref/netstandard1.3/System.IO.dll", + "ref/netstandard1.3/System.IO.xml", + "ref/netstandard1.3/de/System.IO.xml", + "ref/netstandard1.3/es/System.IO.xml", + "ref/netstandard1.3/fr/System.IO.xml", + "ref/netstandard1.3/it/System.IO.xml", + "ref/netstandard1.3/ja/System.IO.xml", + "ref/netstandard1.3/ko/System.IO.xml", + "ref/netstandard1.3/ru/System.IO.xml", + "ref/netstandard1.3/zh-hans/System.IO.xml", + "ref/netstandard1.3/zh-hant/System.IO.xml", + "ref/netstandard1.5/System.IO.dll", + "ref/netstandard1.5/System.IO.xml", + "ref/netstandard1.5/de/System.IO.xml", + "ref/netstandard1.5/es/System.IO.xml", + "ref/netstandard1.5/fr/System.IO.xml", + "ref/netstandard1.5/it/System.IO.xml", + "ref/netstandard1.5/ja/System.IO.xml", + "ref/netstandard1.5/ko/System.IO.xml", + "ref/netstandard1.5/ru/System.IO.xml", + "ref/netstandard1.5/zh-hans/System.IO.xml", + "ref/netstandard1.5/zh-hant/System.IO.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.io.4.3.0.nupkg.sha512", + "system.io.nuspec" + ] + }, + "System.IO.Compression/4.3.0": { + "sha512": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "type": "package", + "path": "system.io.compression/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net46/System.IO.Compression.dll", + "lib/portable-net45+win8+wpa81/_._", + "lib/win8/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net46/System.IO.Compression.dll", + "ref/netcore50/System.IO.Compression.dll", + "ref/netcore50/System.IO.Compression.xml", + "ref/netcore50/de/System.IO.Compression.xml", + "ref/netcore50/es/System.IO.Compression.xml", + "ref/netcore50/fr/System.IO.Compression.xml", + "ref/netcore50/it/System.IO.Compression.xml", + "ref/netcore50/ja/System.IO.Compression.xml", + "ref/netcore50/ko/System.IO.Compression.xml", + "ref/netcore50/ru/System.IO.Compression.xml", + "ref/netcore50/zh-hans/System.IO.Compression.xml", + "ref/netcore50/zh-hant/System.IO.Compression.xml", + "ref/netstandard1.1/System.IO.Compression.dll", + "ref/netstandard1.1/System.IO.Compression.xml", + "ref/netstandard1.1/de/System.IO.Compression.xml", + "ref/netstandard1.1/es/System.IO.Compression.xml", + "ref/netstandard1.1/fr/System.IO.Compression.xml", + "ref/netstandard1.1/it/System.IO.Compression.xml", + "ref/netstandard1.1/ja/System.IO.Compression.xml", + "ref/netstandard1.1/ko/System.IO.Compression.xml", + "ref/netstandard1.1/ru/System.IO.Compression.xml", + "ref/netstandard1.1/zh-hans/System.IO.Compression.xml", + "ref/netstandard1.1/zh-hant/System.IO.Compression.xml", + "ref/netstandard1.3/System.IO.Compression.dll", + "ref/netstandard1.3/System.IO.Compression.xml", + "ref/netstandard1.3/de/System.IO.Compression.xml", + "ref/netstandard1.3/es/System.IO.Compression.xml", + "ref/netstandard1.3/fr/System.IO.Compression.xml", + "ref/netstandard1.3/it/System.IO.Compression.xml", + "ref/netstandard1.3/ja/System.IO.Compression.xml", + "ref/netstandard1.3/ko/System.IO.Compression.xml", + "ref/netstandard1.3/ru/System.IO.Compression.xml", + "ref/netstandard1.3/zh-hans/System.IO.Compression.xml", + "ref/netstandard1.3/zh-hant/System.IO.Compression.xml", + "ref/portable-net45+win8+wpa81/_._", + "ref/win8/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll", + "runtimes/win/lib/net46/System.IO.Compression.dll", + "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll", + "system.io.compression.4.3.0.nupkg.sha512", + "system.io.compression.nuspec" + ] + }, + "System.IO.Compression.ZipFile/4.3.0": { + "sha512": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "type": "package", + "path": "system.io.compression.zipfile/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.IO.Compression.ZipFile.dll", + "lib/netstandard1.3/System.IO.Compression.ZipFile.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.IO.Compression.ZipFile.dll", + "ref/netstandard1.3/System.IO.Compression.ZipFile.dll", + "ref/netstandard1.3/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/de/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/es/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/fr/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/it/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/ja/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/ko/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/ru/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/zh-hans/System.IO.Compression.ZipFile.xml", + "ref/netstandard1.3/zh-hant/System.IO.Compression.ZipFile.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.io.compression.zipfile.4.3.0.nupkg.sha512", + "system.io.compression.zipfile.nuspec" + ] + }, + "System.IO.FileSystem/4.3.0": { + "sha512": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "type": "package", + "path": "system.io.filesystem/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.IO.FileSystem.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.IO.FileSystem.dll", + "ref/netstandard1.3/System.IO.FileSystem.dll", + "ref/netstandard1.3/System.IO.FileSystem.xml", + "ref/netstandard1.3/de/System.IO.FileSystem.xml", + "ref/netstandard1.3/es/System.IO.FileSystem.xml", + "ref/netstandard1.3/fr/System.IO.FileSystem.xml", + "ref/netstandard1.3/it/System.IO.FileSystem.xml", + "ref/netstandard1.3/ja/System.IO.FileSystem.xml", + "ref/netstandard1.3/ko/System.IO.FileSystem.xml", + "ref/netstandard1.3/ru/System.IO.FileSystem.xml", + "ref/netstandard1.3/zh-hans/System.IO.FileSystem.xml", + "ref/netstandard1.3/zh-hant/System.IO.FileSystem.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.io.filesystem.4.3.0.nupkg.sha512", + "system.io.filesystem.nuspec" + ] + }, + "System.IO.FileSystem.Primitives/4.3.0": { + "sha512": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "type": "package", + "path": "system.io.filesystem.primitives/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.IO.FileSystem.Primitives.dll", + "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.IO.FileSystem.Primitives.dll", + "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll", + "ref/netstandard1.3/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/de/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/es/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/fr/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/it/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/ja/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/ko/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/ru/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/zh-hans/System.IO.FileSystem.Primitives.xml", + "ref/netstandard1.3/zh-hant/System.IO.FileSystem.Primitives.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.io.filesystem.primitives.4.3.0.nupkg.sha512", + "system.io.filesystem.primitives.nuspec" + ] + }, + "System.Linq/4.3.0": { + "sha512": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "type": "package", + "path": "system.linq/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net463/System.Linq.dll", + "lib/netcore50/System.Linq.dll", + "lib/netstandard1.6/System.Linq.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net463/System.Linq.dll", + "ref/netcore50/System.Linq.dll", + "ref/netcore50/System.Linq.xml", + "ref/netcore50/de/System.Linq.xml", + "ref/netcore50/es/System.Linq.xml", + "ref/netcore50/fr/System.Linq.xml", + "ref/netcore50/it/System.Linq.xml", + "ref/netcore50/ja/System.Linq.xml", + "ref/netcore50/ko/System.Linq.xml", + "ref/netcore50/ru/System.Linq.xml", + "ref/netcore50/zh-hans/System.Linq.xml", + "ref/netcore50/zh-hant/System.Linq.xml", + "ref/netstandard1.0/System.Linq.dll", + "ref/netstandard1.0/System.Linq.xml", + "ref/netstandard1.0/de/System.Linq.xml", + "ref/netstandard1.0/es/System.Linq.xml", + "ref/netstandard1.0/fr/System.Linq.xml", + "ref/netstandard1.0/it/System.Linq.xml", + "ref/netstandard1.0/ja/System.Linq.xml", + "ref/netstandard1.0/ko/System.Linq.xml", + "ref/netstandard1.0/ru/System.Linq.xml", + "ref/netstandard1.0/zh-hans/System.Linq.xml", + "ref/netstandard1.0/zh-hant/System.Linq.xml", + "ref/netstandard1.6/System.Linq.dll", + "ref/netstandard1.6/System.Linq.xml", + "ref/netstandard1.6/de/System.Linq.xml", + "ref/netstandard1.6/es/System.Linq.xml", + "ref/netstandard1.6/fr/System.Linq.xml", + "ref/netstandard1.6/it/System.Linq.xml", + "ref/netstandard1.6/ja/System.Linq.xml", + "ref/netstandard1.6/ko/System.Linq.xml", + "ref/netstandard1.6/ru/System.Linq.xml", + "ref/netstandard1.6/zh-hans/System.Linq.xml", + "ref/netstandard1.6/zh-hant/System.Linq.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.linq.4.3.0.nupkg.sha512", + "system.linq.nuspec" + ] + }, + "System.Linq.Expressions/4.3.0": { + "sha512": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "type": "package", + "path": "system.linq.expressions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net463/System.Linq.Expressions.dll", + "lib/netcore50/System.Linq.Expressions.dll", + "lib/netstandard1.6/System.Linq.Expressions.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net463/System.Linq.Expressions.dll", + "ref/netcore50/System.Linq.Expressions.dll", + "ref/netcore50/System.Linq.Expressions.xml", + "ref/netcore50/de/System.Linq.Expressions.xml", + "ref/netcore50/es/System.Linq.Expressions.xml", + "ref/netcore50/fr/System.Linq.Expressions.xml", + "ref/netcore50/it/System.Linq.Expressions.xml", + "ref/netcore50/ja/System.Linq.Expressions.xml", + "ref/netcore50/ko/System.Linq.Expressions.xml", + "ref/netcore50/ru/System.Linq.Expressions.xml", + "ref/netcore50/zh-hans/System.Linq.Expressions.xml", + "ref/netcore50/zh-hant/System.Linq.Expressions.xml", + "ref/netstandard1.0/System.Linq.Expressions.dll", + "ref/netstandard1.0/System.Linq.Expressions.xml", + "ref/netstandard1.0/de/System.Linq.Expressions.xml", + "ref/netstandard1.0/es/System.Linq.Expressions.xml", + "ref/netstandard1.0/fr/System.Linq.Expressions.xml", + "ref/netstandard1.0/it/System.Linq.Expressions.xml", + "ref/netstandard1.0/ja/System.Linq.Expressions.xml", + "ref/netstandard1.0/ko/System.Linq.Expressions.xml", + "ref/netstandard1.0/ru/System.Linq.Expressions.xml", + "ref/netstandard1.0/zh-hans/System.Linq.Expressions.xml", + "ref/netstandard1.0/zh-hant/System.Linq.Expressions.xml", + "ref/netstandard1.3/System.Linq.Expressions.dll", + "ref/netstandard1.3/System.Linq.Expressions.xml", + "ref/netstandard1.3/de/System.Linq.Expressions.xml", + "ref/netstandard1.3/es/System.Linq.Expressions.xml", + "ref/netstandard1.3/fr/System.Linq.Expressions.xml", + "ref/netstandard1.3/it/System.Linq.Expressions.xml", + "ref/netstandard1.3/ja/System.Linq.Expressions.xml", + "ref/netstandard1.3/ko/System.Linq.Expressions.xml", + "ref/netstandard1.3/ru/System.Linq.Expressions.xml", + "ref/netstandard1.3/zh-hans/System.Linq.Expressions.xml", + "ref/netstandard1.3/zh-hant/System.Linq.Expressions.xml", + "ref/netstandard1.6/System.Linq.Expressions.dll", + "ref/netstandard1.6/System.Linq.Expressions.xml", + "ref/netstandard1.6/de/System.Linq.Expressions.xml", + "ref/netstandard1.6/es/System.Linq.Expressions.xml", + "ref/netstandard1.6/fr/System.Linq.Expressions.xml", + "ref/netstandard1.6/it/System.Linq.Expressions.xml", + "ref/netstandard1.6/ja/System.Linq.Expressions.xml", + "ref/netstandard1.6/ko/System.Linq.Expressions.xml", + "ref/netstandard1.6/ru/System.Linq.Expressions.xml", + "ref/netstandard1.6/zh-hans/System.Linq.Expressions.xml", + "ref/netstandard1.6/zh-hant/System.Linq.Expressions.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/System.Linq.Expressions.dll", + "system.linq.expressions.4.3.0.nupkg.sha512", + "system.linq.expressions.nuspec" + ] + }, + "System.Memory/4.5.4": { + "sha512": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "type": "package", + "path": "system.memory/4.5.4", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/net461/System.Memory.dll", + "lib/net461/System.Memory.xml", + "lib/netcoreapp2.1/_._", + "lib/netstandard1.1/System.Memory.dll", + "lib/netstandard1.1/System.Memory.xml", + "lib/netstandard2.0/System.Memory.dll", + "lib/netstandard2.0/System.Memory.xml", + "ref/netcoreapp2.1/_._", + "system.memory.4.5.4.nupkg.sha512", + "system.memory.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Net.Http/4.3.4": { + "sha512": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "type": "package", + "path": "system.net.http/4.3.4", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/Xamarinmac20/_._", + "lib/monoandroid10/_._", + "lib/monotouch10/_._", + "lib/net45/_._", + "lib/net46/System.Net.Http.dll", + "lib/portable-net45+win8+wpa81/_._", + "lib/win8/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/Xamarinmac20/_._", + "ref/monoandroid10/_._", + "ref/monotouch10/_._", + "ref/net45/_._", + "ref/net46/System.Net.Http.dll", + "ref/netcore50/System.Net.Http.dll", + "ref/netstandard1.1/System.Net.Http.dll", + "ref/netstandard1.3/System.Net.Http.dll", + "ref/portable-net45+win8+wpa81/_._", + "ref/win8/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll", + "runtimes/win/lib/net46/System.Net.Http.dll", + "runtimes/win/lib/netcore50/System.Net.Http.dll", + "runtimes/win/lib/netstandard1.3/System.Net.Http.dll", + "system.net.http.4.3.4.nupkg.sha512", + "system.net.http.nuspec" + ] + }, + "System.Net.Primitives/4.3.0": { + "sha512": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "type": "package", + "path": "system.net.primitives/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Net.Primitives.dll", + "ref/netcore50/System.Net.Primitives.xml", + "ref/netcore50/de/System.Net.Primitives.xml", + "ref/netcore50/es/System.Net.Primitives.xml", + "ref/netcore50/fr/System.Net.Primitives.xml", + "ref/netcore50/it/System.Net.Primitives.xml", + "ref/netcore50/ja/System.Net.Primitives.xml", + "ref/netcore50/ko/System.Net.Primitives.xml", + "ref/netcore50/ru/System.Net.Primitives.xml", + "ref/netcore50/zh-hans/System.Net.Primitives.xml", + "ref/netcore50/zh-hant/System.Net.Primitives.xml", + "ref/netstandard1.0/System.Net.Primitives.dll", + "ref/netstandard1.0/System.Net.Primitives.xml", + "ref/netstandard1.0/de/System.Net.Primitives.xml", + "ref/netstandard1.0/es/System.Net.Primitives.xml", + "ref/netstandard1.0/fr/System.Net.Primitives.xml", + "ref/netstandard1.0/it/System.Net.Primitives.xml", + "ref/netstandard1.0/ja/System.Net.Primitives.xml", + "ref/netstandard1.0/ko/System.Net.Primitives.xml", + "ref/netstandard1.0/ru/System.Net.Primitives.xml", + "ref/netstandard1.0/zh-hans/System.Net.Primitives.xml", + "ref/netstandard1.0/zh-hant/System.Net.Primitives.xml", + "ref/netstandard1.1/System.Net.Primitives.dll", + "ref/netstandard1.1/System.Net.Primitives.xml", + "ref/netstandard1.1/de/System.Net.Primitives.xml", + "ref/netstandard1.1/es/System.Net.Primitives.xml", + "ref/netstandard1.1/fr/System.Net.Primitives.xml", + "ref/netstandard1.1/it/System.Net.Primitives.xml", + "ref/netstandard1.1/ja/System.Net.Primitives.xml", + "ref/netstandard1.1/ko/System.Net.Primitives.xml", + "ref/netstandard1.1/ru/System.Net.Primitives.xml", + "ref/netstandard1.1/zh-hans/System.Net.Primitives.xml", + "ref/netstandard1.1/zh-hant/System.Net.Primitives.xml", + "ref/netstandard1.3/System.Net.Primitives.dll", + "ref/netstandard1.3/System.Net.Primitives.xml", + "ref/netstandard1.3/de/System.Net.Primitives.xml", + "ref/netstandard1.3/es/System.Net.Primitives.xml", + "ref/netstandard1.3/fr/System.Net.Primitives.xml", + "ref/netstandard1.3/it/System.Net.Primitives.xml", + "ref/netstandard1.3/ja/System.Net.Primitives.xml", + "ref/netstandard1.3/ko/System.Net.Primitives.xml", + "ref/netstandard1.3/ru/System.Net.Primitives.xml", + "ref/netstandard1.3/zh-hans/System.Net.Primitives.xml", + "ref/netstandard1.3/zh-hant/System.Net.Primitives.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.net.primitives.4.3.0.nupkg.sha512", + "system.net.primitives.nuspec" + ] + }, + "System.Net.Sockets/4.3.0": { + "sha512": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "type": "package", + "path": "system.net.sockets/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Net.Sockets.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Net.Sockets.dll", + "ref/netstandard1.3/System.Net.Sockets.dll", + "ref/netstandard1.3/System.Net.Sockets.xml", + "ref/netstandard1.3/de/System.Net.Sockets.xml", + "ref/netstandard1.3/es/System.Net.Sockets.xml", + "ref/netstandard1.3/fr/System.Net.Sockets.xml", + "ref/netstandard1.3/it/System.Net.Sockets.xml", + "ref/netstandard1.3/ja/System.Net.Sockets.xml", + "ref/netstandard1.3/ko/System.Net.Sockets.xml", + "ref/netstandard1.3/ru/System.Net.Sockets.xml", + "ref/netstandard1.3/zh-hans/System.Net.Sockets.xml", + "ref/netstandard1.3/zh-hant/System.Net.Sockets.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.net.sockets.4.3.0.nupkg.sha512", + "system.net.sockets.nuspec" + ] + }, + "System.ObjectModel/4.3.0": { + "sha512": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "type": "package", + "path": "system.objectmodel/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.ObjectModel.dll", + "lib/netstandard1.3/System.ObjectModel.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.ObjectModel.dll", + "ref/netcore50/System.ObjectModel.xml", + "ref/netcore50/de/System.ObjectModel.xml", + "ref/netcore50/es/System.ObjectModel.xml", + "ref/netcore50/fr/System.ObjectModel.xml", + "ref/netcore50/it/System.ObjectModel.xml", + "ref/netcore50/ja/System.ObjectModel.xml", + "ref/netcore50/ko/System.ObjectModel.xml", + "ref/netcore50/ru/System.ObjectModel.xml", + "ref/netcore50/zh-hans/System.ObjectModel.xml", + "ref/netcore50/zh-hant/System.ObjectModel.xml", + "ref/netstandard1.0/System.ObjectModel.dll", + "ref/netstandard1.0/System.ObjectModel.xml", + "ref/netstandard1.0/de/System.ObjectModel.xml", + "ref/netstandard1.0/es/System.ObjectModel.xml", + "ref/netstandard1.0/fr/System.ObjectModel.xml", + "ref/netstandard1.0/it/System.ObjectModel.xml", + "ref/netstandard1.0/ja/System.ObjectModel.xml", + "ref/netstandard1.0/ko/System.ObjectModel.xml", + "ref/netstandard1.0/ru/System.ObjectModel.xml", + "ref/netstandard1.0/zh-hans/System.ObjectModel.xml", + "ref/netstandard1.0/zh-hant/System.ObjectModel.xml", + "ref/netstandard1.3/System.ObjectModel.dll", + "ref/netstandard1.3/System.ObjectModel.xml", + "ref/netstandard1.3/de/System.ObjectModel.xml", + "ref/netstandard1.3/es/System.ObjectModel.xml", + "ref/netstandard1.3/fr/System.ObjectModel.xml", + "ref/netstandard1.3/it/System.ObjectModel.xml", + "ref/netstandard1.3/ja/System.ObjectModel.xml", + "ref/netstandard1.3/ko/System.ObjectModel.xml", + "ref/netstandard1.3/ru/System.ObjectModel.xml", + "ref/netstandard1.3/zh-hans/System.ObjectModel.xml", + "ref/netstandard1.3/zh-hant/System.ObjectModel.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.objectmodel.4.3.0.nupkg.sha512", + "system.objectmodel.nuspec" + ] + }, + "System.Reflection/4.3.0": { + "sha512": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "type": "package", + "path": "system.reflection/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net462/System.Reflection.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net462/System.Reflection.dll", + "ref/netcore50/System.Reflection.dll", + "ref/netcore50/System.Reflection.xml", + "ref/netcore50/de/System.Reflection.xml", + "ref/netcore50/es/System.Reflection.xml", + "ref/netcore50/fr/System.Reflection.xml", + "ref/netcore50/it/System.Reflection.xml", + "ref/netcore50/ja/System.Reflection.xml", + "ref/netcore50/ko/System.Reflection.xml", + "ref/netcore50/ru/System.Reflection.xml", + "ref/netcore50/zh-hans/System.Reflection.xml", + "ref/netcore50/zh-hant/System.Reflection.xml", + "ref/netstandard1.0/System.Reflection.dll", + "ref/netstandard1.0/System.Reflection.xml", + "ref/netstandard1.0/de/System.Reflection.xml", + "ref/netstandard1.0/es/System.Reflection.xml", + "ref/netstandard1.0/fr/System.Reflection.xml", + "ref/netstandard1.0/it/System.Reflection.xml", + "ref/netstandard1.0/ja/System.Reflection.xml", + "ref/netstandard1.0/ko/System.Reflection.xml", + "ref/netstandard1.0/ru/System.Reflection.xml", + "ref/netstandard1.0/zh-hans/System.Reflection.xml", + "ref/netstandard1.0/zh-hant/System.Reflection.xml", + "ref/netstandard1.3/System.Reflection.dll", + "ref/netstandard1.3/System.Reflection.xml", + "ref/netstandard1.3/de/System.Reflection.xml", + "ref/netstandard1.3/es/System.Reflection.xml", + "ref/netstandard1.3/fr/System.Reflection.xml", + "ref/netstandard1.3/it/System.Reflection.xml", + "ref/netstandard1.3/ja/System.Reflection.xml", + "ref/netstandard1.3/ko/System.Reflection.xml", + "ref/netstandard1.3/ru/System.Reflection.xml", + "ref/netstandard1.3/zh-hans/System.Reflection.xml", + "ref/netstandard1.3/zh-hant/System.Reflection.xml", + "ref/netstandard1.5/System.Reflection.dll", + "ref/netstandard1.5/System.Reflection.xml", + "ref/netstandard1.5/de/System.Reflection.xml", + "ref/netstandard1.5/es/System.Reflection.xml", + "ref/netstandard1.5/fr/System.Reflection.xml", + "ref/netstandard1.5/it/System.Reflection.xml", + "ref/netstandard1.5/ja/System.Reflection.xml", + "ref/netstandard1.5/ko/System.Reflection.xml", + "ref/netstandard1.5/ru/System.Reflection.xml", + "ref/netstandard1.5/zh-hans/System.Reflection.xml", + "ref/netstandard1.5/zh-hant/System.Reflection.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.reflection.4.3.0.nupkg.sha512", + "system.reflection.nuspec" + ] + }, + "System.Reflection.Emit/4.3.0": { + "sha512": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "type": "package", + "path": "system.reflection.emit/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/monotouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Reflection.Emit.dll", + "lib/netstandard1.3/System.Reflection.Emit.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/net45/_._", + "ref/netstandard1.1/System.Reflection.Emit.dll", + "ref/netstandard1.1/System.Reflection.Emit.xml", + "ref/netstandard1.1/de/System.Reflection.Emit.xml", + "ref/netstandard1.1/es/System.Reflection.Emit.xml", + "ref/netstandard1.1/fr/System.Reflection.Emit.xml", + "ref/netstandard1.1/it/System.Reflection.Emit.xml", + "ref/netstandard1.1/ja/System.Reflection.Emit.xml", + "ref/netstandard1.1/ko/System.Reflection.Emit.xml", + "ref/netstandard1.1/ru/System.Reflection.Emit.xml", + "ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml", + "ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml", + "ref/xamarinmac20/_._", + "system.reflection.emit.4.3.0.nupkg.sha512", + "system.reflection.emit.nuspec" + ] + }, + "System.Reflection.Emit.ILGeneration/4.3.0": { + "sha512": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "type": "package", + "path": "system.reflection.emit.ilgeneration/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Reflection.Emit.ILGeneration.dll", + "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll", + "lib/portable-net45+wp8/_._", + "lib/wp80/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.dll", + "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/de/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/es/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/fr/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/it/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/ja/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/ko/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/ru/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/zh-hans/System.Reflection.Emit.ILGeneration.xml", + "ref/netstandard1.0/zh-hant/System.Reflection.Emit.ILGeneration.xml", + "ref/portable-net45+wp8/_._", + "ref/wp80/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/_._", + "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512", + "system.reflection.emit.ilgeneration.nuspec" + ] + }, + "System.Reflection.Emit.Lightweight/4.3.0": { + "sha512": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "type": "package", + "path": "system.reflection.emit.lightweight/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Reflection.Emit.Lightweight.dll", + "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll", + "lib/portable-net45+wp8/_._", + "lib/wp80/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netstandard1.0/System.Reflection.Emit.Lightweight.dll", + "ref/netstandard1.0/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/de/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/es/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/fr/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/it/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/ja/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/ko/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/ru/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/zh-hans/System.Reflection.Emit.Lightweight.xml", + "ref/netstandard1.0/zh-hant/System.Reflection.Emit.Lightweight.xml", + "ref/portable-net45+wp8/_._", + "ref/wp80/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/_._", + "system.reflection.emit.lightweight.4.3.0.nupkg.sha512", + "system.reflection.emit.lightweight.nuspec" + ] + }, + "System.Reflection.Extensions/4.3.0": { + "sha512": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "type": "package", + "path": "system.reflection.extensions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Reflection.Extensions.dll", + "ref/netcore50/System.Reflection.Extensions.xml", + "ref/netcore50/de/System.Reflection.Extensions.xml", + "ref/netcore50/es/System.Reflection.Extensions.xml", + "ref/netcore50/fr/System.Reflection.Extensions.xml", + "ref/netcore50/it/System.Reflection.Extensions.xml", + "ref/netcore50/ja/System.Reflection.Extensions.xml", + "ref/netcore50/ko/System.Reflection.Extensions.xml", + "ref/netcore50/ru/System.Reflection.Extensions.xml", + "ref/netcore50/zh-hans/System.Reflection.Extensions.xml", + "ref/netcore50/zh-hant/System.Reflection.Extensions.xml", + "ref/netstandard1.0/System.Reflection.Extensions.dll", + "ref/netstandard1.0/System.Reflection.Extensions.xml", + "ref/netstandard1.0/de/System.Reflection.Extensions.xml", + "ref/netstandard1.0/es/System.Reflection.Extensions.xml", + "ref/netstandard1.0/fr/System.Reflection.Extensions.xml", + "ref/netstandard1.0/it/System.Reflection.Extensions.xml", + "ref/netstandard1.0/ja/System.Reflection.Extensions.xml", + "ref/netstandard1.0/ko/System.Reflection.Extensions.xml", + "ref/netstandard1.0/ru/System.Reflection.Extensions.xml", + "ref/netstandard1.0/zh-hans/System.Reflection.Extensions.xml", + "ref/netstandard1.0/zh-hant/System.Reflection.Extensions.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.reflection.extensions.4.3.0.nupkg.sha512", + "system.reflection.extensions.nuspec" + ] + }, + "System.Reflection.Metadata/1.6.0": { + "sha512": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==", + "type": "package", + "path": "system.reflection.metadata/1.6.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netstandard1.1/System.Reflection.Metadata.dll", + "lib/netstandard1.1/System.Reflection.Metadata.xml", + "lib/netstandard2.0/System.Reflection.Metadata.dll", + "lib/netstandard2.0/System.Reflection.Metadata.xml", + "lib/portable-net45+win8/System.Reflection.Metadata.dll", + "lib/portable-net45+win8/System.Reflection.Metadata.xml", + "system.reflection.metadata.1.6.0.nupkg.sha512", + "system.reflection.metadata.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Reflection.Primitives/4.3.0": { + "sha512": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "type": "package", + "path": "system.reflection.primitives/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Reflection.Primitives.dll", + "ref/netcore50/System.Reflection.Primitives.xml", + "ref/netcore50/de/System.Reflection.Primitives.xml", + "ref/netcore50/es/System.Reflection.Primitives.xml", + "ref/netcore50/fr/System.Reflection.Primitives.xml", + "ref/netcore50/it/System.Reflection.Primitives.xml", + "ref/netcore50/ja/System.Reflection.Primitives.xml", + "ref/netcore50/ko/System.Reflection.Primitives.xml", + "ref/netcore50/ru/System.Reflection.Primitives.xml", + "ref/netcore50/zh-hans/System.Reflection.Primitives.xml", + "ref/netcore50/zh-hant/System.Reflection.Primitives.xml", + "ref/netstandard1.0/System.Reflection.Primitives.dll", + "ref/netstandard1.0/System.Reflection.Primitives.xml", + "ref/netstandard1.0/de/System.Reflection.Primitives.xml", + "ref/netstandard1.0/es/System.Reflection.Primitives.xml", + "ref/netstandard1.0/fr/System.Reflection.Primitives.xml", + "ref/netstandard1.0/it/System.Reflection.Primitives.xml", + "ref/netstandard1.0/ja/System.Reflection.Primitives.xml", + "ref/netstandard1.0/ko/System.Reflection.Primitives.xml", + "ref/netstandard1.0/ru/System.Reflection.Primitives.xml", + "ref/netstandard1.0/zh-hans/System.Reflection.Primitives.xml", + "ref/netstandard1.0/zh-hant/System.Reflection.Primitives.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.reflection.primitives.4.3.0.nupkg.sha512", + "system.reflection.primitives.nuspec" + ] + }, + "System.Reflection.TypeExtensions/4.3.0": { + "sha512": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "type": "package", + "path": "system.reflection.typeextensions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Reflection.TypeExtensions.dll", + "lib/net462/System.Reflection.TypeExtensions.dll", + "lib/netcore50/System.Reflection.TypeExtensions.dll", + "lib/netstandard1.5/System.Reflection.TypeExtensions.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Reflection.TypeExtensions.dll", + "ref/net462/System.Reflection.TypeExtensions.dll", + "ref/netstandard1.3/System.Reflection.TypeExtensions.dll", + "ref/netstandard1.3/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/de/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/es/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/fr/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/it/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/ja/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/ko/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/ru/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/zh-hans/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.3/zh-hant/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/System.Reflection.TypeExtensions.dll", + "ref/netstandard1.5/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/de/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/es/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/fr/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/it/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/ja/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/ko/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/ru/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/zh-hans/System.Reflection.TypeExtensions.xml", + "ref/netstandard1.5/zh-hant/System.Reflection.TypeExtensions.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/System.Reflection.TypeExtensions.dll", + "system.reflection.typeextensions.4.3.0.nupkg.sha512", + "system.reflection.typeextensions.nuspec" + ] + }, + "System.Resources.ResourceManager/4.3.0": { + "sha512": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "type": "package", + "path": "system.resources.resourcemanager/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Resources.ResourceManager.dll", + "ref/netcore50/System.Resources.ResourceManager.xml", + "ref/netcore50/de/System.Resources.ResourceManager.xml", + "ref/netcore50/es/System.Resources.ResourceManager.xml", + "ref/netcore50/fr/System.Resources.ResourceManager.xml", + "ref/netcore50/it/System.Resources.ResourceManager.xml", + "ref/netcore50/ja/System.Resources.ResourceManager.xml", + "ref/netcore50/ko/System.Resources.ResourceManager.xml", + "ref/netcore50/ru/System.Resources.ResourceManager.xml", + "ref/netcore50/zh-hans/System.Resources.ResourceManager.xml", + "ref/netcore50/zh-hant/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/System.Resources.ResourceManager.dll", + "ref/netstandard1.0/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/de/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/es/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/fr/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/it/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/ja/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/ko/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/ru/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/zh-hans/System.Resources.ResourceManager.xml", + "ref/netstandard1.0/zh-hant/System.Resources.ResourceManager.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.resources.resourcemanager.4.3.0.nupkg.sha512", + "system.resources.resourcemanager.nuspec" + ] + }, + "System.Runtime/4.3.0": { + "sha512": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "type": "package", + "path": "system.runtime/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net462/System.Runtime.dll", + "lib/portable-net45+win8+wp80+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net462/System.Runtime.dll", + "ref/netcore50/System.Runtime.dll", + "ref/netcore50/System.Runtime.xml", + "ref/netcore50/de/System.Runtime.xml", + "ref/netcore50/es/System.Runtime.xml", + "ref/netcore50/fr/System.Runtime.xml", + "ref/netcore50/it/System.Runtime.xml", + "ref/netcore50/ja/System.Runtime.xml", + "ref/netcore50/ko/System.Runtime.xml", + "ref/netcore50/ru/System.Runtime.xml", + "ref/netcore50/zh-hans/System.Runtime.xml", + "ref/netcore50/zh-hant/System.Runtime.xml", + "ref/netstandard1.0/System.Runtime.dll", + "ref/netstandard1.0/System.Runtime.xml", + "ref/netstandard1.0/de/System.Runtime.xml", + "ref/netstandard1.0/es/System.Runtime.xml", + "ref/netstandard1.0/fr/System.Runtime.xml", + "ref/netstandard1.0/it/System.Runtime.xml", + "ref/netstandard1.0/ja/System.Runtime.xml", + "ref/netstandard1.0/ko/System.Runtime.xml", + "ref/netstandard1.0/ru/System.Runtime.xml", + "ref/netstandard1.0/zh-hans/System.Runtime.xml", + "ref/netstandard1.0/zh-hant/System.Runtime.xml", + "ref/netstandard1.2/System.Runtime.dll", + "ref/netstandard1.2/System.Runtime.xml", + "ref/netstandard1.2/de/System.Runtime.xml", + "ref/netstandard1.2/es/System.Runtime.xml", + "ref/netstandard1.2/fr/System.Runtime.xml", + "ref/netstandard1.2/it/System.Runtime.xml", + "ref/netstandard1.2/ja/System.Runtime.xml", + "ref/netstandard1.2/ko/System.Runtime.xml", + "ref/netstandard1.2/ru/System.Runtime.xml", + "ref/netstandard1.2/zh-hans/System.Runtime.xml", + "ref/netstandard1.2/zh-hant/System.Runtime.xml", + "ref/netstandard1.3/System.Runtime.dll", + "ref/netstandard1.3/System.Runtime.xml", + "ref/netstandard1.3/de/System.Runtime.xml", + "ref/netstandard1.3/es/System.Runtime.xml", + "ref/netstandard1.3/fr/System.Runtime.xml", + "ref/netstandard1.3/it/System.Runtime.xml", + "ref/netstandard1.3/ja/System.Runtime.xml", + "ref/netstandard1.3/ko/System.Runtime.xml", + "ref/netstandard1.3/ru/System.Runtime.xml", + "ref/netstandard1.3/zh-hans/System.Runtime.xml", + "ref/netstandard1.3/zh-hant/System.Runtime.xml", + "ref/netstandard1.5/System.Runtime.dll", + "ref/netstandard1.5/System.Runtime.xml", + "ref/netstandard1.5/de/System.Runtime.xml", + "ref/netstandard1.5/es/System.Runtime.xml", + "ref/netstandard1.5/fr/System.Runtime.xml", + "ref/netstandard1.5/it/System.Runtime.xml", + "ref/netstandard1.5/ja/System.Runtime.xml", + "ref/netstandard1.5/ko/System.Runtime.xml", + "ref/netstandard1.5/ru/System.Runtime.xml", + "ref/netstandard1.5/zh-hans/System.Runtime.xml", + "ref/netstandard1.5/zh-hant/System.Runtime.xml", + "ref/portable-net45+win8+wp80+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.runtime.4.3.0.nupkg.sha512", + "system.runtime.nuspec" + ] + }, + "System.Runtime.Extensions/4.3.0": { + "sha512": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "type": "package", + "path": "system.runtime.extensions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net462/System.Runtime.Extensions.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net462/System.Runtime.Extensions.dll", + "ref/netcore50/System.Runtime.Extensions.dll", + "ref/netcore50/System.Runtime.Extensions.xml", + "ref/netcore50/de/System.Runtime.Extensions.xml", + "ref/netcore50/es/System.Runtime.Extensions.xml", + "ref/netcore50/fr/System.Runtime.Extensions.xml", + "ref/netcore50/it/System.Runtime.Extensions.xml", + "ref/netcore50/ja/System.Runtime.Extensions.xml", + "ref/netcore50/ko/System.Runtime.Extensions.xml", + "ref/netcore50/ru/System.Runtime.Extensions.xml", + "ref/netcore50/zh-hans/System.Runtime.Extensions.xml", + "ref/netcore50/zh-hant/System.Runtime.Extensions.xml", + "ref/netstandard1.0/System.Runtime.Extensions.dll", + "ref/netstandard1.0/System.Runtime.Extensions.xml", + "ref/netstandard1.0/de/System.Runtime.Extensions.xml", + "ref/netstandard1.0/es/System.Runtime.Extensions.xml", + "ref/netstandard1.0/fr/System.Runtime.Extensions.xml", + "ref/netstandard1.0/it/System.Runtime.Extensions.xml", + "ref/netstandard1.0/ja/System.Runtime.Extensions.xml", + "ref/netstandard1.0/ko/System.Runtime.Extensions.xml", + "ref/netstandard1.0/ru/System.Runtime.Extensions.xml", + "ref/netstandard1.0/zh-hans/System.Runtime.Extensions.xml", + "ref/netstandard1.0/zh-hant/System.Runtime.Extensions.xml", + "ref/netstandard1.3/System.Runtime.Extensions.dll", + "ref/netstandard1.3/System.Runtime.Extensions.xml", + "ref/netstandard1.3/de/System.Runtime.Extensions.xml", + "ref/netstandard1.3/es/System.Runtime.Extensions.xml", + "ref/netstandard1.3/fr/System.Runtime.Extensions.xml", + "ref/netstandard1.3/it/System.Runtime.Extensions.xml", + "ref/netstandard1.3/ja/System.Runtime.Extensions.xml", + "ref/netstandard1.3/ko/System.Runtime.Extensions.xml", + "ref/netstandard1.3/ru/System.Runtime.Extensions.xml", + "ref/netstandard1.3/zh-hans/System.Runtime.Extensions.xml", + "ref/netstandard1.3/zh-hant/System.Runtime.Extensions.xml", + "ref/netstandard1.5/System.Runtime.Extensions.dll", + "ref/netstandard1.5/System.Runtime.Extensions.xml", + "ref/netstandard1.5/de/System.Runtime.Extensions.xml", + "ref/netstandard1.5/es/System.Runtime.Extensions.xml", + "ref/netstandard1.5/fr/System.Runtime.Extensions.xml", + "ref/netstandard1.5/it/System.Runtime.Extensions.xml", + "ref/netstandard1.5/ja/System.Runtime.Extensions.xml", + "ref/netstandard1.5/ko/System.Runtime.Extensions.xml", + "ref/netstandard1.5/ru/System.Runtime.Extensions.xml", + "ref/netstandard1.5/zh-hans/System.Runtime.Extensions.xml", + "ref/netstandard1.5/zh-hant/System.Runtime.Extensions.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.runtime.extensions.4.3.0.nupkg.sha512", + "system.runtime.extensions.nuspec" + ] + }, + "System.Runtime.Handles/4.3.0": { + "sha512": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "type": "package", + "path": "system.runtime.handles/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/_._", + "ref/netstandard1.3/System.Runtime.Handles.dll", + "ref/netstandard1.3/System.Runtime.Handles.xml", + "ref/netstandard1.3/de/System.Runtime.Handles.xml", + "ref/netstandard1.3/es/System.Runtime.Handles.xml", + "ref/netstandard1.3/fr/System.Runtime.Handles.xml", + "ref/netstandard1.3/it/System.Runtime.Handles.xml", + "ref/netstandard1.3/ja/System.Runtime.Handles.xml", + "ref/netstandard1.3/ko/System.Runtime.Handles.xml", + "ref/netstandard1.3/ru/System.Runtime.Handles.xml", + "ref/netstandard1.3/zh-hans/System.Runtime.Handles.xml", + "ref/netstandard1.3/zh-hant/System.Runtime.Handles.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.runtime.handles.4.3.0.nupkg.sha512", + "system.runtime.handles.nuspec" + ] + }, + "System.Runtime.InteropServices/4.3.0": { + "sha512": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "type": "package", + "path": "system.runtime.interopservices/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net462/System.Runtime.InteropServices.dll", + "lib/net463/System.Runtime.InteropServices.dll", + "lib/portable-net45+win8+wpa81/_._", + "lib/win8/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net462/System.Runtime.InteropServices.dll", + "ref/net463/System.Runtime.InteropServices.dll", + "ref/netcore50/System.Runtime.InteropServices.dll", + "ref/netcore50/System.Runtime.InteropServices.xml", + "ref/netcore50/de/System.Runtime.InteropServices.xml", + "ref/netcore50/es/System.Runtime.InteropServices.xml", + "ref/netcore50/fr/System.Runtime.InteropServices.xml", + "ref/netcore50/it/System.Runtime.InteropServices.xml", + "ref/netcore50/ja/System.Runtime.InteropServices.xml", + "ref/netcore50/ko/System.Runtime.InteropServices.xml", + "ref/netcore50/ru/System.Runtime.InteropServices.xml", + "ref/netcore50/zh-hans/System.Runtime.InteropServices.xml", + "ref/netcore50/zh-hant/System.Runtime.InteropServices.xml", + "ref/netcoreapp1.1/System.Runtime.InteropServices.dll", + "ref/netstandard1.1/System.Runtime.InteropServices.dll", + "ref/netstandard1.1/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/de/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/es/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/fr/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/it/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/ja/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/ko/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/ru/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/zh-hans/System.Runtime.InteropServices.xml", + "ref/netstandard1.1/zh-hant/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/System.Runtime.InteropServices.dll", + "ref/netstandard1.2/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/de/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/es/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/fr/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/it/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/ja/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/ko/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/ru/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/zh-hans/System.Runtime.InteropServices.xml", + "ref/netstandard1.2/zh-hant/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/System.Runtime.InteropServices.dll", + "ref/netstandard1.3/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/de/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/es/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/fr/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/it/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/ja/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/ko/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/ru/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/zh-hans/System.Runtime.InteropServices.xml", + "ref/netstandard1.3/zh-hant/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/System.Runtime.InteropServices.dll", + "ref/netstandard1.5/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/de/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/es/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/fr/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/it/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/ja/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/ko/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/ru/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/zh-hans/System.Runtime.InteropServices.xml", + "ref/netstandard1.5/zh-hant/System.Runtime.InteropServices.xml", + "ref/portable-net45+win8+wpa81/_._", + "ref/win8/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.runtime.interopservices.4.3.0.nupkg.sha512", + "system.runtime.interopservices.nuspec" + ] + }, + "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { + "sha512": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "type": "package", + "path": "system.runtime.interopservices.runtimeinformation/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", + "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", + "lib/win8/System.Runtime.InteropServices.RuntimeInformation.dll", + "lib/wpa81/System.Runtime.InteropServices.RuntimeInformation.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", + "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", + "runtimes/win/lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", + "runtimes/win/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", + "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", + "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512", + "system.runtime.interopservices.runtimeinformation.nuspec" + ] + }, + "System.Runtime.Numerics/4.3.0": { + "sha512": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "type": "package", + "path": "system.runtime.numerics/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Runtime.Numerics.dll", + "lib/netstandard1.3/System.Runtime.Numerics.dll", + "lib/portable-net45+win8+wpa81/_._", + "lib/win8/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Runtime.Numerics.dll", + "ref/netcore50/System.Runtime.Numerics.xml", + "ref/netcore50/de/System.Runtime.Numerics.xml", + "ref/netcore50/es/System.Runtime.Numerics.xml", + "ref/netcore50/fr/System.Runtime.Numerics.xml", + "ref/netcore50/it/System.Runtime.Numerics.xml", + "ref/netcore50/ja/System.Runtime.Numerics.xml", + "ref/netcore50/ko/System.Runtime.Numerics.xml", + "ref/netcore50/ru/System.Runtime.Numerics.xml", + "ref/netcore50/zh-hans/System.Runtime.Numerics.xml", + "ref/netcore50/zh-hant/System.Runtime.Numerics.xml", + "ref/netstandard1.1/System.Runtime.Numerics.dll", + "ref/netstandard1.1/System.Runtime.Numerics.xml", + "ref/netstandard1.1/de/System.Runtime.Numerics.xml", + "ref/netstandard1.1/es/System.Runtime.Numerics.xml", + "ref/netstandard1.1/fr/System.Runtime.Numerics.xml", + "ref/netstandard1.1/it/System.Runtime.Numerics.xml", + "ref/netstandard1.1/ja/System.Runtime.Numerics.xml", + "ref/netstandard1.1/ko/System.Runtime.Numerics.xml", + "ref/netstandard1.1/ru/System.Runtime.Numerics.xml", + "ref/netstandard1.1/zh-hans/System.Runtime.Numerics.xml", + "ref/netstandard1.1/zh-hant/System.Runtime.Numerics.xml", + "ref/portable-net45+win8+wpa81/_._", + "ref/win8/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.runtime.numerics.4.3.0.nupkg.sha512", + "system.runtime.numerics.nuspec" + ] + }, + "System.Security.Cryptography.Algorithms/4.3.0": { + "sha512": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "type": "package", + "path": "system.security.cryptography.algorithms/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Security.Cryptography.Algorithms.dll", + "lib/net461/System.Security.Cryptography.Algorithms.dll", + "lib/net463/System.Security.Cryptography.Algorithms.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Security.Cryptography.Algorithms.dll", + "ref/net461/System.Security.Cryptography.Algorithms.dll", + "ref/net463/System.Security.Cryptography.Algorithms.dll", + "ref/netstandard1.3/System.Security.Cryptography.Algorithms.dll", + "ref/netstandard1.4/System.Security.Cryptography.Algorithms.dll", + "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", + "runtimes/win/lib/net46/System.Security.Cryptography.Algorithms.dll", + "runtimes/win/lib/net461/System.Security.Cryptography.Algorithms.dll", + "runtimes/win/lib/net463/System.Security.Cryptography.Algorithms.dll", + "runtimes/win/lib/netcore50/System.Security.Cryptography.Algorithms.dll", + "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", + "system.security.cryptography.algorithms.4.3.0.nupkg.sha512", + "system.security.cryptography.algorithms.nuspec" + ] + }, + "System.Security.Cryptography.Cng/4.3.0": { + "sha512": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", + "type": "package", + "path": "system.security.cryptography.cng/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/net46/System.Security.Cryptography.Cng.dll", + "lib/net461/System.Security.Cryptography.Cng.dll", + "lib/net463/System.Security.Cryptography.Cng.dll", + "ref/net46/System.Security.Cryptography.Cng.dll", + "ref/net461/System.Security.Cryptography.Cng.dll", + "ref/net463/System.Security.Cryptography.Cng.dll", + "ref/netstandard1.3/System.Security.Cryptography.Cng.dll", + "ref/netstandard1.4/System.Security.Cryptography.Cng.dll", + "ref/netstandard1.6/System.Security.Cryptography.Cng.dll", + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", + "runtimes/win/lib/net46/System.Security.Cryptography.Cng.dll", + "runtimes/win/lib/net461/System.Security.Cryptography.Cng.dll", + "runtimes/win/lib/net463/System.Security.Cryptography.Cng.dll", + "runtimes/win/lib/netstandard1.4/System.Security.Cryptography.Cng.dll", + "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", + "system.security.cryptography.cng.4.3.0.nupkg.sha512", + "system.security.cryptography.cng.nuspec" + ] + }, + "System.Security.Cryptography.Csp/4.3.0": { + "sha512": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "type": "package", + "path": "system.security.cryptography.csp/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Security.Cryptography.Csp.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Security.Cryptography.Csp.dll", + "ref/netstandard1.3/System.Security.Cryptography.Csp.dll", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", + "runtimes/win/lib/net46/System.Security.Cryptography.Csp.dll", + "runtimes/win/lib/netcore50/_._", + "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", + "system.security.cryptography.csp.4.3.0.nupkg.sha512", + "system.security.cryptography.csp.nuspec" + ] + }, + "System.Security.Cryptography.Encoding/4.3.0": { + "sha512": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "type": "package", + "path": "system.security.cryptography.encoding/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Security.Cryptography.Encoding.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Security.Cryptography.Encoding.dll", + "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll", + "ref/netstandard1.3/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/de/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/es/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/fr/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/it/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/ja/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/ko/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/ru/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/zh-hans/System.Security.Cryptography.Encoding.xml", + "ref/netstandard1.3/zh-hant/System.Security.Cryptography.Encoding.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", + "runtimes/win/lib/net46/System.Security.Cryptography.Encoding.dll", + "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", + "system.security.cryptography.encoding.4.3.0.nupkg.sha512", + "system.security.cryptography.encoding.nuspec" + ] + }, + "System.Security.Cryptography.OpenSsl/4.3.0": { + "sha512": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "type": "package", + "path": "system.security.cryptography.openssl/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", + "ref/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", + "system.security.cryptography.openssl.4.3.0.nupkg.sha512", + "system.security.cryptography.openssl.nuspec" + ] + }, + "System.Security.Cryptography.Primitives/4.3.0": { + "sha512": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "type": "package", + "path": "system.security.cryptography.primitives/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Security.Cryptography.Primitives.dll", + "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Security.Cryptography.Primitives.dll", + "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.security.cryptography.primitives.4.3.0.nupkg.sha512", + "system.security.cryptography.primitives.nuspec" + ] + }, + "System.Security.Cryptography.X509Certificates/4.3.0": { + "sha512": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "type": "package", + "path": "system.security.cryptography.x509certificates/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net46/System.Security.Cryptography.X509Certificates.dll", + "lib/net461/System.Security.Cryptography.X509Certificates.dll", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net46/System.Security.Cryptography.X509Certificates.dll", + "ref/net461/System.Security.Cryptography.X509Certificates.dll", + "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.dll", + "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/de/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/es/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/fr/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/it/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/ja/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/ko/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/ru/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/zh-hans/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.3/zh-hant/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll", + "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/de/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/es/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/fr/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/it/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/ja/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/ko/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/ru/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/zh-hans/System.Security.Cryptography.X509Certificates.xml", + "ref/netstandard1.4/zh-hant/System.Security.Cryptography.X509Certificates.xml", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", + "runtimes/win/lib/net46/System.Security.Cryptography.X509Certificates.dll", + "runtimes/win/lib/net461/System.Security.Cryptography.X509Certificates.dll", + "runtimes/win/lib/netcore50/System.Security.Cryptography.X509Certificates.dll", + "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", + "system.security.cryptography.x509certificates.4.3.0.nupkg.sha512", + "system.security.cryptography.x509certificates.nuspec" + ] + }, + "System.Text.Encoding/4.3.0": { + "sha512": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "type": "package", + "path": "system.text.encoding/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Text.Encoding.dll", + "ref/netcore50/System.Text.Encoding.xml", + "ref/netcore50/de/System.Text.Encoding.xml", + "ref/netcore50/es/System.Text.Encoding.xml", + "ref/netcore50/fr/System.Text.Encoding.xml", + "ref/netcore50/it/System.Text.Encoding.xml", + "ref/netcore50/ja/System.Text.Encoding.xml", + "ref/netcore50/ko/System.Text.Encoding.xml", + "ref/netcore50/ru/System.Text.Encoding.xml", + "ref/netcore50/zh-hans/System.Text.Encoding.xml", + "ref/netcore50/zh-hant/System.Text.Encoding.xml", + "ref/netstandard1.0/System.Text.Encoding.dll", + "ref/netstandard1.0/System.Text.Encoding.xml", + "ref/netstandard1.0/de/System.Text.Encoding.xml", + "ref/netstandard1.0/es/System.Text.Encoding.xml", + "ref/netstandard1.0/fr/System.Text.Encoding.xml", + "ref/netstandard1.0/it/System.Text.Encoding.xml", + "ref/netstandard1.0/ja/System.Text.Encoding.xml", + "ref/netstandard1.0/ko/System.Text.Encoding.xml", + "ref/netstandard1.0/ru/System.Text.Encoding.xml", + "ref/netstandard1.0/zh-hans/System.Text.Encoding.xml", + "ref/netstandard1.0/zh-hant/System.Text.Encoding.xml", + "ref/netstandard1.3/System.Text.Encoding.dll", + "ref/netstandard1.3/System.Text.Encoding.xml", + "ref/netstandard1.3/de/System.Text.Encoding.xml", + "ref/netstandard1.3/es/System.Text.Encoding.xml", + "ref/netstandard1.3/fr/System.Text.Encoding.xml", + "ref/netstandard1.3/it/System.Text.Encoding.xml", + "ref/netstandard1.3/ja/System.Text.Encoding.xml", + "ref/netstandard1.3/ko/System.Text.Encoding.xml", + "ref/netstandard1.3/ru/System.Text.Encoding.xml", + "ref/netstandard1.3/zh-hans/System.Text.Encoding.xml", + "ref/netstandard1.3/zh-hant/System.Text.Encoding.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.text.encoding.4.3.0.nupkg.sha512", + "system.text.encoding.nuspec" + ] + }, + "System.Text.Encoding.Extensions/4.3.0": { + "sha512": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "type": "package", + "path": "system.text.encoding.extensions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Text.Encoding.Extensions.dll", + "ref/netcore50/System.Text.Encoding.Extensions.xml", + "ref/netcore50/de/System.Text.Encoding.Extensions.xml", + "ref/netcore50/es/System.Text.Encoding.Extensions.xml", + "ref/netcore50/fr/System.Text.Encoding.Extensions.xml", + "ref/netcore50/it/System.Text.Encoding.Extensions.xml", + "ref/netcore50/ja/System.Text.Encoding.Extensions.xml", + "ref/netcore50/ko/System.Text.Encoding.Extensions.xml", + "ref/netcore50/ru/System.Text.Encoding.Extensions.xml", + "ref/netcore50/zh-hans/System.Text.Encoding.Extensions.xml", + "ref/netcore50/zh-hant/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/System.Text.Encoding.Extensions.dll", + "ref/netstandard1.0/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/de/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/es/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/fr/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/it/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/ja/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/ko/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/ru/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/zh-hans/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.0/zh-hant/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/System.Text.Encoding.Extensions.dll", + "ref/netstandard1.3/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/de/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/es/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/fr/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/it/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/ja/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/ko/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/ru/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/zh-hans/System.Text.Encoding.Extensions.xml", + "ref/netstandard1.3/zh-hant/System.Text.Encoding.Extensions.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.text.encoding.extensions.4.3.0.nupkg.sha512", + "system.text.encoding.extensions.nuspec" + ] + }, + "System.Text.Encodings.Web/4.5.0": { + "sha512": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==", + "type": "package", + "path": "system.text.encodings.web/4.5.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.TXT", + "THIRD-PARTY-NOTICES.TXT", + "lib/netstandard1.0/System.Text.Encodings.Web.dll", + "lib/netstandard1.0/System.Text.Encodings.Web.xml", + "lib/netstandard2.0/System.Text.Encodings.Web.dll", + "lib/netstandard2.0/System.Text.Encodings.Web.xml", + "system.text.encodings.web.4.5.0.nupkg.sha512", + "system.text.encodings.web.nuspec", + "useSharedDesignerContext.txt", + "version.txt" + ] + }, + "System.Text.RegularExpressions/4.3.0": { + "sha512": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "type": "package", + "path": "system.text.regularexpressions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net463/System.Text.RegularExpressions.dll", + "lib/netcore50/System.Text.RegularExpressions.dll", + "lib/netstandard1.6/System.Text.RegularExpressions.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net463/System.Text.RegularExpressions.dll", + "ref/netcore50/System.Text.RegularExpressions.dll", + "ref/netcore50/System.Text.RegularExpressions.xml", + "ref/netcore50/de/System.Text.RegularExpressions.xml", + "ref/netcore50/es/System.Text.RegularExpressions.xml", + "ref/netcore50/fr/System.Text.RegularExpressions.xml", + "ref/netcore50/it/System.Text.RegularExpressions.xml", + "ref/netcore50/ja/System.Text.RegularExpressions.xml", + "ref/netcore50/ko/System.Text.RegularExpressions.xml", + "ref/netcore50/ru/System.Text.RegularExpressions.xml", + "ref/netcore50/zh-hans/System.Text.RegularExpressions.xml", + "ref/netcore50/zh-hant/System.Text.RegularExpressions.xml", + "ref/netcoreapp1.1/System.Text.RegularExpressions.dll", + "ref/netstandard1.0/System.Text.RegularExpressions.dll", + "ref/netstandard1.0/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/de/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/es/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/fr/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/it/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/ja/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/ko/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/ru/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/zh-hans/System.Text.RegularExpressions.xml", + "ref/netstandard1.0/zh-hant/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/System.Text.RegularExpressions.dll", + "ref/netstandard1.3/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/de/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/es/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/fr/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/it/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/ja/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/ko/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/ru/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/zh-hans/System.Text.RegularExpressions.xml", + "ref/netstandard1.3/zh-hant/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/System.Text.RegularExpressions.dll", + "ref/netstandard1.6/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/de/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/es/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/fr/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/it/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/ja/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/ko/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/ru/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/zh-hans/System.Text.RegularExpressions.xml", + "ref/netstandard1.6/zh-hant/System.Text.RegularExpressions.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.text.regularexpressions.4.3.0.nupkg.sha512", + "system.text.regularexpressions.nuspec" + ] + }, + "System.Threading/4.3.0": { + "sha512": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "type": "package", + "path": "system.threading/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Threading.dll", + "lib/netstandard1.3/System.Threading.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Threading.dll", + "ref/netcore50/System.Threading.xml", + "ref/netcore50/de/System.Threading.xml", + "ref/netcore50/es/System.Threading.xml", + "ref/netcore50/fr/System.Threading.xml", + "ref/netcore50/it/System.Threading.xml", + "ref/netcore50/ja/System.Threading.xml", + "ref/netcore50/ko/System.Threading.xml", + "ref/netcore50/ru/System.Threading.xml", + "ref/netcore50/zh-hans/System.Threading.xml", + "ref/netcore50/zh-hant/System.Threading.xml", + "ref/netstandard1.0/System.Threading.dll", + "ref/netstandard1.0/System.Threading.xml", + "ref/netstandard1.0/de/System.Threading.xml", + "ref/netstandard1.0/es/System.Threading.xml", + "ref/netstandard1.0/fr/System.Threading.xml", + "ref/netstandard1.0/it/System.Threading.xml", + "ref/netstandard1.0/ja/System.Threading.xml", + "ref/netstandard1.0/ko/System.Threading.xml", + "ref/netstandard1.0/ru/System.Threading.xml", + "ref/netstandard1.0/zh-hans/System.Threading.xml", + "ref/netstandard1.0/zh-hant/System.Threading.xml", + "ref/netstandard1.3/System.Threading.dll", + "ref/netstandard1.3/System.Threading.xml", + "ref/netstandard1.3/de/System.Threading.xml", + "ref/netstandard1.3/es/System.Threading.xml", + "ref/netstandard1.3/fr/System.Threading.xml", + "ref/netstandard1.3/it/System.Threading.xml", + "ref/netstandard1.3/ja/System.Threading.xml", + "ref/netstandard1.3/ko/System.Threading.xml", + "ref/netstandard1.3/ru/System.Threading.xml", + "ref/netstandard1.3/zh-hans/System.Threading.xml", + "ref/netstandard1.3/zh-hant/System.Threading.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "runtimes/aot/lib/netcore50/System.Threading.dll", + "system.threading.4.3.0.nupkg.sha512", + "system.threading.nuspec" + ] + }, + "System.Threading.Tasks/4.3.0": { + "sha512": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "type": "package", + "path": "system.threading.tasks/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Threading.Tasks.dll", + "ref/netcore50/System.Threading.Tasks.xml", + "ref/netcore50/de/System.Threading.Tasks.xml", + "ref/netcore50/es/System.Threading.Tasks.xml", + "ref/netcore50/fr/System.Threading.Tasks.xml", + "ref/netcore50/it/System.Threading.Tasks.xml", + "ref/netcore50/ja/System.Threading.Tasks.xml", + "ref/netcore50/ko/System.Threading.Tasks.xml", + "ref/netcore50/ru/System.Threading.Tasks.xml", + "ref/netcore50/zh-hans/System.Threading.Tasks.xml", + "ref/netcore50/zh-hant/System.Threading.Tasks.xml", + "ref/netstandard1.0/System.Threading.Tasks.dll", + "ref/netstandard1.0/System.Threading.Tasks.xml", + "ref/netstandard1.0/de/System.Threading.Tasks.xml", + "ref/netstandard1.0/es/System.Threading.Tasks.xml", + "ref/netstandard1.0/fr/System.Threading.Tasks.xml", + "ref/netstandard1.0/it/System.Threading.Tasks.xml", + "ref/netstandard1.0/ja/System.Threading.Tasks.xml", + "ref/netstandard1.0/ko/System.Threading.Tasks.xml", + "ref/netstandard1.0/ru/System.Threading.Tasks.xml", + "ref/netstandard1.0/zh-hans/System.Threading.Tasks.xml", + "ref/netstandard1.0/zh-hant/System.Threading.Tasks.xml", + "ref/netstandard1.3/System.Threading.Tasks.dll", + "ref/netstandard1.3/System.Threading.Tasks.xml", + "ref/netstandard1.3/de/System.Threading.Tasks.xml", + "ref/netstandard1.3/es/System.Threading.Tasks.xml", + "ref/netstandard1.3/fr/System.Threading.Tasks.xml", + "ref/netstandard1.3/it/System.Threading.Tasks.xml", + "ref/netstandard1.3/ja/System.Threading.Tasks.xml", + "ref/netstandard1.3/ko/System.Threading.Tasks.xml", + "ref/netstandard1.3/ru/System.Threading.Tasks.xml", + "ref/netstandard1.3/zh-hans/System.Threading.Tasks.xml", + "ref/netstandard1.3/zh-hant/System.Threading.Tasks.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.threading.tasks.4.3.0.nupkg.sha512", + "system.threading.tasks.nuspec" + ] + }, + "System.Threading.Tasks.Extensions/4.3.0": { + "sha512": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", + "type": "package", + "path": "system.threading.tasks.extensions/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll", + "lib/netstandard1.0/System.Threading.Tasks.Extensions.xml", + "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.dll", + "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.xml", + "system.threading.tasks.extensions.4.3.0.nupkg.sha512", + "system.threading.tasks.extensions.nuspec" + ] + }, + "System.Threading.Timer/4.3.0": { + "sha512": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "type": "package", + "path": "system.threading.timer/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net451/_._", + "lib/portable-net451+win81+wpa81/_._", + "lib/win81/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net451/_._", + "ref/netcore50/System.Threading.Timer.dll", + "ref/netcore50/System.Threading.Timer.xml", + "ref/netcore50/de/System.Threading.Timer.xml", + "ref/netcore50/es/System.Threading.Timer.xml", + "ref/netcore50/fr/System.Threading.Timer.xml", + "ref/netcore50/it/System.Threading.Timer.xml", + "ref/netcore50/ja/System.Threading.Timer.xml", + "ref/netcore50/ko/System.Threading.Timer.xml", + "ref/netcore50/ru/System.Threading.Timer.xml", + "ref/netcore50/zh-hans/System.Threading.Timer.xml", + "ref/netcore50/zh-hant/System.Threading.Timer.xml", + "ref/netstandard1.2/System.Threading.Timer.dll", + "ref/netstandard1.2/System.Threading.Timer.xml", + "ref/netstandard1.2/de/System.Threading.Timer.xml", + "ref/netstandard1.2/es/System.Threading.Timer.xml", + "ref/netstandard1.2/fr/System.Threading.Timer.xml", + "ref/netstandard1.2/it/System.Threading.Timer.xml", + "ref/netstandard1.2/ja/System.Threading.Timer.xml", + "ref/netstandard1.2/ko/System.Threading.Timer.xml", + "ref/netstandard1.2/ru/System.Threading.Timer.xml", + "ref/netstandard1.2/zh-hans/System.Threading.Timer.xml", + "ref/netstandard1.2/zh-hant/System.Threading.Timer.xml", + "ref/portable-net451+win81+wpa81/_._", + "ref/win81/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.threading.timer.4.3.0.nupkg.sha512", + "system.threading.timer.nuspec" + ] + }, + "System.Xml.ReaderWriter/4.3.0": { + "sha512": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "type": "package", + "path": "system.xml.readerwriter/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/net46/System.Xml.ReaderWriter.dll", + "lib/netcore50/System.Xml.ReaderWriter.dll", + "lib/netstandard1.3/System.Xml.ReaderWriter.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/net46/System.Xml.ReaderWriter.dll", + "ref/netcore50/System.Xml.ReaderWriter.dll", + "ref/netcore50/System.Xml.ReaderWriter.xml", + "ref/netcore50/de/System.Xml.ReaderWriter.xml", + "ref/netcore50/es/System.Xml.ReaderWriter.xml", + "ref/netcore50/fr/System.Xml.ReaderWriter.xml", + "ref/netcore50/it/System.Xml.ReaderWriter.xml", + "ref/netcore50/ja/System.Xml.ReaderWriter.xml", + "ref/netcore50/ko/System.Xml.ReaderWriter.xml", + "ref/netcore50/ru/System.Xml.ReaderWriter.xml", + "ref/netcore50/zh-hans/System.Xml.ReaderWriter.xml", + "ref/netcore50/zh-hant/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/System.Xml.ReaderWriter.dll", + "ref/netstandard1.0/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/de/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/es/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/fr/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/it/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/ja/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/ko/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/ru/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/zh-hans/System.Xml.ReaderWriter.xml", + "ref/netstandard1.0/zh-hant/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/System.Xml.ReaderWriter.dll", + "ref/netstandard1.3/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/de/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/es/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/fr/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/it/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/ja/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/ko/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/ru/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/zh-hans/System.Xml.ReaderWriter.xml", + "ref/netstandard1.3/zh-hant/System.Xml.ReaderWriter.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.xml.readerwriter.4.3.0.nupkg.sha512", + "system.xml.readerwriter.nuspec" + ] + }, + "System.Xml.XDocument/4.3.0": { + "sha512": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "type": "package", + "path": "system.xml.xdocument/4.3.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "ThirdPartyNotices.txt", + "dotnet_library_license.txt", + "lib/MonoAndroid10/_._", + "lib/MonoTouch10/_._", + "lib/net45/_._", + "lib/netcore50/System.Xml.XDocument.dll", + "lib/netstandard1.3/System.Xml.XDocument.dll", + "lib/portable-net45+win8+wp8+wpa81/_._", + "lib/win8/_._", + "lib/wp80/_._", + "lib/wpa81/_._", + "lib/xamarinios10/_._", + "lib/xamarinmac20/_._", + "lib/xamarintvos10/_._", + "lib/xamarinwatchos10/_._", + "ref/MonoAndroid10/_._", + "ref/MonoTouch10/_._", + "ref/net45/_._", + "ref/netcore50/System.Xml.XDocument.dll", + "ref/netcore50/System.Xml.XDocument.xml", + "ref/netcore50/de/System.Xml.XDocument.xml", + "ref/netcore50/es/System.Xml.XDocument.xml", + "ref/netcore50/fr/System.Xml.XDocument.xml", + "ref/netcore50/it/System.Xml.XDocument.xml", + "ref/netcore50/ja/System.Xml.XDocument.xml", + "ref/netcore50/ko/System.Xml.XDocument.xml", + "ref/netcore50/ru/System.Xml.XDocument.xml", + "ref/netcore50/zh-hans/System.Xml.XDocument.xml", + "ref/netcore50/zh-hant/System.Xml.XDocument.xml", + "ref/netstandard1.0/System.Xml.XDocument.dll", + "ref/netstandard1.0/System.Xml.XDocument.xml", + "ref/netstandard1.0/de/System.Xml.XDocument.xml", + "ref/netstandard1.0/es/System.Xml.XDocument.xml", + "ref/netstandard1.0/fr/System.Xml.XDocument.xml", + "ref/netstandard1.0/it/System.Xml.XDocument.xml", + "ref/netstandard1.0/ja/System.Xml.XDocument.xml", + "ref/netstandard1.0/ko/System.Xml.XDocument.xml", + "ref/netstandard1.0/ru/System.Xml.XDocument.xml", + "ref/netstandard1.0/zh-hans/System.Xml.XDocument.xml", + "ref/netstandard1.0/zh-hant/System.Xml.XDocument.xml", + "ref/netstandard1.3/System.Xml.XDocument.dll", + "ref/netstandard1.3/System.Xml.XDocument.xml", + "ref/netstandard1.3/de/System.Xml.XDocument.xml", + "ref/netstandard1.3/es/System.Xml.XDocument.xml", + "ref/netstandard1.3/fr/System.Xml.XDocument.xml", + "ref/netstandard1.3/it/System.Xml.XDocument.xml", + "ref/netstandard1.3/ja/System.Xml.XDocument.xml", + "ref/netstandard1.3/ko/System.Xml.XDocument.xml", + "ref/netstandard1.3/ru/System.Xml.XDocument.xml", + "ref/netstandard1.3/zh-hans/System.Xml.XDocument.xml", + "ref/netstandard1.3/zh-hant/System.Xml.XDocument.xml", + "ref/portable-net45+win8+wp8+wpa81/_._", + "ref/win8/_._", + "ref/wp80/_._", + "ref/wpa81/_._", + "ref/xamarinios10/_._", + "ref/xamarinmac20/_._", + "ref/xamarintvos10/_._", + "ref/xamarinwatchos10/_._", + "system.xml.xdocument.4.3.0.nupkg.sha512", + "system.xml.xdocument.nuspec" + ] + }, + "Yoti/3.6.0": { + "sha512": "qtZ5CPwzAZX344iFIQ7lA3zI686/KsE8YW9PL1ZDUzYaNrt6TrIEwA03sHEp4GoQSSdyDgCKmhk2X2XA09Pwtg==", + "type": "package", + "path": "yoti/3.6.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "Yoti.png", + "lib/net452/Yoti.Auth.dll", + "lib/net462/Yoti.Auth.dll", + "lib/net472/Yoti.Auth.dll", + "lib/net48/Yoti.Auth.dll", + "lib/netcoreapp1.1/Yoti.Auth.dll", + "lib/netcoreapp2.2/Yoti.Auth.dll", + "lib/netcoreapp3.1/Yoti.Auth.dll", + "lib/netstandard1.6/Yoti.Auth.dll", + "lib/netstandard2.1/Yoti.Auth.dll", + "yoti.3.6.0.nupkg.sha512", + "yoti.nuspec" + ] + } + }, + "projectFileDependencyGroups": { + ".NETCoreApp,Version=v3.1": [ + "DotNetEnv >= 1.4.0", + "Microsoft.AspNetCore.Hosting >= 2.2.7", + "Microsoft.Extensions.DependencyInjection >= 3.1.8", + "Microsoft.Extensions.Logging >= 3.1.8", + "Microsoft.Extensions.Logging.Console >= 3.1.8", + "Yoti >= 3.6.0", + "dotenv.net >= 2.1.1" + ] + }, + "packageFolders": { + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", + "projectName": "CoreExample", + "projectPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", + "packagesPath": "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages", + "outputPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/obj/", + "projectStyle": "PackageReference", + "configFilePaths": [ + "/home/runner/.nuget/NuGet/NuGet.Config" + ], + "originalTargetFrameworks": [ + "netcoreapp3.1" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "netcoreapp3.1": { + "targetAlias": "netcoreapp3.1", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "10.0.100" + }, + "frameworks": { + "netcoreapp3.1": { + "targetAlias": "netcoreapp3.1", + "dependencies": { + "DotNetEnv": { + "target": "Package", + "version": "[1.4.0, )" + }, + "Microsoft.AspNetCore.Hosting": { + "target": "Package", + "version": "[2.2.7, )" + }, + "Microsoft.Extensions.DependencyInjection": { + "target": "Package", + "version": "[3.1.8, )" + }, + "Microsoft.Extensions.Logging": { + "target": "Package", + "version": "[3.1.8, )" + }, + "Microsoft.Extensions.Logging.Console": { + "target": "Package", + "version": "[3.1.8, )" + }, + "Yoti": { + "target": "Package", + "version": "[3.6.0, )" + }, + "dotenv.net": { + "target": "Package", + "version": "[2.1.1, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "downloadDependencies": [ + { + "name": "Microsoft.AspNetCore.App.Ref", + "version": "[3.1.10, 3.1.10]" + }, + { + "name": "Microsoft.NETCore.App.Host.linux-x64", + "version": "[3.1.32, 3.1.32]" + }, + { + "name": "Microsoft.NETCore.App.Ref", + "version": "[3.1.0, 3.1.0]" + } + ], + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/10.0.100/RuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/project.nuget.cache b/dotnet/CoreExample/CoreExample/obj/project.nuget.cache new file mode 100644 index 0000000..3e1c616 --- /dev/null +++ b/dotnet/CoreExample/CoreExample/obj/project.nuget.cache @@ -0,0 +1,124 @@ +{ + "version": 2, + "dgSpecHash": "My0XGmEiEkQ=", + "success": true, + "projectFilePath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", + "expectedPackageFiles": [ + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/dotenv.net/2.1.1/dotenv.net.2.1.1.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/dotnetenv/1.4.0/dotnetenv.1.4.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/google.protobuf/3.12.3/google.protobuf.3.12.3.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/jsonsubtypes/1.7.0/jsonsubtypes.1.7.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.hosting/2.2.7/microsoft.aspnetcore.hosting.2.2.7.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.hosting.abstractions/2.2.0/microsoft.aspnetcore.hosting.abstractions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.hosting.server.abstractions/2.2.0/microsoft.aspnetcore.hosting.server.abstractions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http/2.2.0/microsoft.aspnetcore.http.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http.abstractions/2.2.0/microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http.extensions/2.2.0/microsoft.aspnetcore.http.extensions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http.features/2.2.0/microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.webutilities/2.2.0/microsoft.aspnetcore.webutilities.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration/3.1.8/microsoft.extensions.configuration.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.abstractions/3.1.8/microsoft.extensions.configuration.abstractions.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.binder/3.1.8/microsoft.extensions.configuration.binder.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.environmentvariables/2.2.4/microsoft.extensions.configuration.environmentvariables.2.2.4.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.fileextensions/2.2.0/microsoft.extensions.configuration.fileextensions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.dependencyinjection/3.1.8/microsoft.extensions.dependencyinjection.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.dependencyinjection.abstractions/3.1.8/microsoft.extensions.dependencyinjection.abstractions.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.fileproviders.abstractions/2.2.0/microsoft.extensions.fileproviders.abstractions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.fileproviders.physical/2.2.0/microsoft.extensions.fileproviders.physical.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.filesystemglobbing/2.2.0/microsoft.extensions.filesystemglobbing.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.hosting.abstractions/2.2.0/microsoft.extensions.hosting.abstractions.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging/3.1.8/microsoft.extensions.logging.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging.abstractions/3.1.8/microsoft.extensions.logging.abstractions.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging.configuration/3.1.8/microsoft.extensions.logging.configuration.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging.console/3.1.8/microsoft.extensions.logging.console.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.objectpool/2.2.0/microsoft.extensions.objectpool.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.options/3.1.8/microsoft.extensions.options.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.options.configurationextensions/3.1.8/microsoft.extensions.options.configurationextensions.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.primitives/3.1.8/microsoft.extensions.primitives.3.1.8.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.net.http.headers/2.2.0/microsoft.net.http.headers.2.2.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.platforms/1.1.1/microsoft.netcore.platforms.1.1.1.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.targets/1.1.0/microsoft.netcore.targets.1.1.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.win32.primitives/4.3.0/microsoft.win32.primitives.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/netstandard.library/1.6.1/netstandard.library.1.6.1.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/newtonsoft.json/12.0.3/newtonsoft.json.12.0.3.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/nlog/4.7.2/nlog.4.7.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/portable.bouncycastle/1.8.5/portable.bouncycastle.1.8.5.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system/4.3.0/runtime.native.system.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.io.compression/4.3.0/runtime.native.system.io.compression.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.net.http/4.3.0/runtime.native.system.net.http.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.security.cryptography.apple/4.3.0/runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.security.cryptography.openssl/4.3.2/runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.appcontext/4.3.0/system.appcontext.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.buffers/4.5.0/system.buffers.4.5.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.collections/4.3.0/system.collections.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.collections.concurrent/4.3.0/system.collections.concurrent.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.console/4.3.0/system.console.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.debug/4.3.0/system.diagnostics.debug.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.diagnosticsource/4.5.1/system.diagnostics.diagnosticsource.4.5.1.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.tools/4.3.0/system.diagnostics.tools.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.tracing/4.3.0/system.diagnostics.tracing.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.globalization/4.3.0/system.globalization.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.globalization.calendars/4.3.0/system.globalization.calendars.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.globalization.extensions/4.3.0/system.globalization.extensions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io/4.3.0/system.io.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.compression/4.3.0/system.io.compression.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.compression.zipfile/4.3.0/system.io.compression.zipfile.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.filesystem/4.3.0/system.io.filesystem.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.filesystem.primitives/4.3.0/system.io.filesystem.primitives.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.linq/4.3.0/system.linq.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.linq.expressions/4.3.0/system.linq.expressions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.memory/4.5.4/system.memory.4.5.4.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.net.http/4.3.4/system.net.http.4.3.4.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.net.primitives/4.3.0/system.net.primitives.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.net.sockets/4.3.0/system.net.sockets.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.objectmodel/4.3.0/system.objectmodel.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection/4.3.0/system.reflection.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.emit/4.3.0/system.reflection.emit.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.emit.ilgeneration/4.3.0/system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.emit.lightweight/4.3.0/system.reflection.emit.lightweight.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.extensions/4.3.0/system.reflection.extensions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.metadata/1.6.0/system.reflection.metadata.1.6.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.primitives/4.3.0/system.reflection.primitives.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.typeextensions/4.3.0/system.reflection.typeextensions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.resources.resourcemanager/4.3.0/system.resources.resourcemanager.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime/4.3.0/system.runtime.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.extensions/4.3.0/system.runtime.extensions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.handles/4.3.0/system.runtime.handles.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.interopservices/4.3.0/system.runtime.interopservices.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.interopservices.runtimeinformation/4.3.0/system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.numerics/4.3.0/system.runtime.numerics.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.algorithms/4.3.0/system.security.cryptography.algorithms.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.cng/4.3.0/system.security.cryptography.cng.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.csp/4.3.0/system.security.cryptography.csp.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.encoding/4.3.0/system.security.cryptography.encoding.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.openssl/4.3.0/system.security.cryptography.openssl.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.primitives/4.3.0/system.security.cryptography.primitives.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.x509certificates/4.3.0/system.security.cryptography.x509certificates.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.encoding/4.3.0/system.text.encoding.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.encoding.extensions/4.3.0/system.text.encoding.extensions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.encodings.web/4.5.0/system.text.encodings.web.4.5.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.regularexpressions/4.3.0/system.text.regularexpressions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading/4.3.0/system.threading.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading.tasks/4.3.0/system.threading.tasks.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading.tasks.extensions/4.3.0/system.threading.tasks.extensions.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading.timer/4.3.0/system.threading.timer.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.xml.readerwriter/4.3.0/system.xml.readerwriter.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.xml.xdocument/4.3.0/system.xml.xdocument.4.3.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/yoti/3.6.0/yoti.3.6.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.app.ref/3.1.0/microsoft.netcore.app.ref.3.1.0.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.app.ref/3.1.10/microsoft.aspnetcore.app.ref.3.1.10.nupkg.sha512", + "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.app.host.linux-x64/3.1.32/microsoft.netcore.app.host.linux-x64.3.1.32.nupkg.sha512" + ], + "logs": [] +} \ No newline at end of file diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/list b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/list new file mode 100644 index 0000000..f77833d --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/list @@ -0,0 +1,2 @@ +v1.1.0 +v2.3.1+incompatible diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v1.1.0.mod b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v1.1.0.mod new file mode 100644 index 0000000..53d9407 --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v1.1.0.mod @@ -0,0 +1 @@ +module github.com/getyoti/yoti-go-sdk diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.info b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.info new file mode 100644 index 0000000..49483d5 --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.info @@ -0,0 +1 @@ +{"Version":"v2.3.1+incompatible","Time":"2019-02-28T12:31:44Z"} \ No newline at end of file diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.lock b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.lock new file mode 100644 index 0000000..e69de29 diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.mod b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.mod new file mode 100644 index 0000000..53d9407 --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.mod @@ -0,0 +1 @@ +module github.com/getyoti/yoti-go-sdk diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.zip b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.zip new file mode 100644 index 0000000000000000000000000000000000000000..593830a5c2b56d777d4cf9c31b9cd0e6a5b09613 GIT binary patch literal 170732 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

6r%YcZoipto(Pd8Oqcf+^8Pg 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ǔ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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.ziphash b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.ziphash new file mode 100644 index 0000000..ae8337a --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/@v/v2.3.1+incompatible.ziphash @@ -0,0 +1 @@ +h1:+hiLmgo7aT1q1psL5ypahU4jHeUfWnIjF9t4CgJQSes= \ No newline at end of file diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/list b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/list new file mode 100644 index 0000000..5ef3202 --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/list @@ -0,0 +1 @@ +v3.14.0 diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.info b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.info new file mode 100644 index 0000000..56a19a6 --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.info @@ -0,0 +1 @@ +{"Version":"v3.14.0","Time":"2025-06-12T08:13:54Z","Origin":{"VCS":"git","URL":"https://github.com/getyoti/yoti-go-sdk","Hash":"84a18a5bb476a96652219fa7bfbe68f037781a54","Ref":"refs/tags/v3.14.0"}} \ No newline at end of file diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.lock b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.lock new file mode 100644 index 0000000..e69de29 diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.mod b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.mod new file mode 100644 index 0000000..c1c87ab --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.mod @@ -0,0 +1,10 @@ +module github.com/getyoti/yoti-go-sdk/v3 + +require ( + google.golang.org/protobuf v1.28.0 + gotest.tools/v3 v3.3.0 +) + +require github.com/google/go-cmp v0.5.5 // indirect + +go 1.19 diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.zip b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..0a45b992c9f664d3196422696b1b5d77d46d174e GIT binary patch literal 630802 zcmbrk18^_V@-7<{~ny*FD$HFE!~XiO&shPEX-WJ99*p!{&AtRaG-NB zwPA2)5p-vvXJVyiWT5|^YUJwbY-Q}`YUZM>?np~Fsv#*wCnZO(L@8B8sU$f*Aw{Py zIU%D&DM=?i&M>BSG1si>%l8K{cMDas0|iqYGdhQ{^E4KSjLURN8h@ExRV`c$U? zAzMI*yx_d``sdV@m6C?Y6etiaB40wk+WeB3hjPa_l|t z8|p=3QZeq=*dXi=>Fd+YqZg9OLd5w8Pd@{Hg%d2n zj6<-W!{y~vn+_UX>Rz;WNiLQGHhrp=T-Rk!pX|>+@jgNRsgn%)t)!xFiO>G6P6Yoe zotT3J)K+}gWo({lX^?o^*h*ISe3@gt!z$kqPhMs6wHcgz30wP;&WASG zd-u&T)E#CY)n~YAdcpXoGLp1vzw*9C1^ioODEwE-@NjUpF}HQ_aA7cUFg0_sr8BZO zvh{MYa`}gDVpJ#NaKsS1094ZLk^l>z6iLS$tB_XPP*FtO1jSHmb8B<%)2m;X6-_Kq ztxnTHIKq2uJ8qu9ou{$;${_6U?v#m4i)&hD^YI}fD z1bNzTr*R0f^)SBnwT}xqR1W3$Uc?J0LK`3P=SLG@+>+ zLErqb(mIMF;!`V%xV5n*s`H^QI2>;}@3m@()KdhW5}yfafVq3n4o^qo~97aA}R z8D$xjk{VHB3xA#2d&FZT`*>`X3EAi;n??_P0GTy`z(FKHcXD*;<;n6k$>CAlYZvL! zMn~DSHUDw)HtLvN-z+M3bZ7){Xsq^7IBdx(R~}P#`1zFaMzn*2$JKJo1u;u1q*{TO z6;krF#G}HFGB2y16qqB1$AJJtFyb?Etz@8Mn^=9|-yDN3EEqPnWgGQiD^(0Xb=IMK zN)ToI0{UQ_WLN!SxM~oL^-5iob7aYIcXW42qZBPjLF0}QGYD~~O$k)l4-iy%#L>jy zv(})*NTX>KlZMp_t=*eJe^pqEGq!GZvwZLPp-||I6{- z(sS6fvb8~L^VBck^I_PayE}xAC!$MVYai;Z$4)RkvvgUMscX{V z?F=U&t}L{HBM&3g~;yHS+tW=R@3{IoZ&9^Lt>x16sT5 zjk8Su(w%=c0cBMzMP|>u+8rBb_56~J1Ux+#&=M8;$=QkS9c~h);jh~wg!f=-K4P-X zyFLG%>jBf$l$D!DOw6r9LNN49eDChUP zFFPz^DZ@HdE2s{`d(gFsZSXzkKVkKWGq@A%de;4mN9Sx}Gl%4p|oe7uUzz^Y+EX!{cWRa0Yt^B2{nb!cxeqYYLw6 z@#toQT{!8adq{SMjxKta;1{X8RT`1L)i z0B98FsmSi_dr2WER+Cz1P|E;<$nB~wt2xn@_z2{fz?bR-@PwDIz6Wub2 z*e&kO!$ZLg6Il6uMx&gN%4WN+T4A%}M=u^3%TC}0Fg8`0ELX%QXDNn11!hE?Jk#$M zUdUe~KCHOcNLGfoW4D@7ekQlAm4Qn(=}o^`?e6{%WvZD3bA0Kf4G^~nBdYjp|VUNE_%fB`(>woOt zRu=XS&StW)b@qdVuwotoVI{U|A_0XuO}~9adP-EG#D@+dy90=&i<|ML_(qN>!k5bxN#ik>YW!`{7!NytjXuM z?iI9cdbV4XfVW$}87i&!YTxJ>WhwJl7z!u9q%C2;+OAt5a1n)%A8XDu&i!;Htt$Wk z2Q>XH&;O>a1Xp{3x!+GG-{W7)|DQ5!2U{b13lppV1!YqeK8H<4*o_Yw_-PRx!fE?( z_($}7EN)c1H&AG?-*$!dri!ZAUjX4x@2PXkJ5LW@6|xF`&V8TpMO$$V$vZh<%6QZy z_7~>Df%_~h3JEx3s);RHpg1O_2~fdE2i(bMl6P9r)a` z689`@)zX!P$E*EcPN}2@rIy|mb~kv>QZddqZmrV#+iDo(-VOABT~iO-Wi&e&%rKAL zO=!TpG4x|x?!h~SPvvlQ)fd3X{4xK6x)LTxBF3OLkjaXp>Lujdkf`}R)+(^)voSb| z$|NVSNnak$lZVnp_8(N>FH-98Bz%M*3xGKhkK?VYIA-O-k%+k>i^q&oSyW*rX+{m! zy(Ft3JERzlP9l_A#c^1})skATm@so^JGaO0TN(8xBh z6rLOXFp8pBYF8ZlGNFn>A*YdzqdSHn>Bik!8p1{X#IEt`L@n3e+2)zyJbe#L<4(@T zcO*$MvKy+B{SyfYxVtbx_SQIPk!$4I;VMQDR?J*|*2?xF2<^eH%QmaCSdY+{+m9Aq z0$UbVQ-kEjRWR416!^7g@IPQFCS9l*{&tqye;WmbzUTi3>6oLl8QpiW zviM)9$ySlEUuQ(zJV!;Kp0O3C7W)N5%C4JFVPn+qGvxg2VZ$3Qcs1Hx*=W4*v_O-PA2N$=orvb=jf&uh+n-CQ9BS zG~dFFt)h=7)_|~vM3%`SSqQ7v!STmLrZ#Ho)VFj!ntFB%CyEkV1NzWMuzf5mT2TEP zFx-z&UU6FWofg68pSs6+k2zoP{|pVAahDbW-_{2G&T;>BXyE#ftSzD-uc|C1tfnd@ zFY%qn>c;We_cOwVdF1Q?YwhFa+eB33IRkywkt=1Qib9J!7?i4@kWQnJ? zhMlC%l|TUKgQl%vT1#CmoK&g92i=`<=T-YGmtn>Ci8%FshaAVE4%$VPr`PW+c&o&HMDG@Pwm477mx=Q2r z=j@1^_cZXz`IewWezwq!G~$JHh0Iop4{n_)WZcF?$>EY5H`^E_JkRWUPMlq{Et649%y6#-P5k2zGat&I3nC$%>g$Fm8%JE0eS5|aPUL}H>RW!+mmJ}_;xV*iX zgWr*lw|jbhNsn3WW)YPJWi#((l(XXi%;~)3mjU@@t{2ueFc#6`7Rt}dH(7>8|EPJ? zZ7OYo{v9f%w;U!OZr9K)?jXkS*QJS?Z<>389W{>?bje(r#pP!lg=3XXD>3t&3JB-Br*BXkEOU zQdKqJ+vTO7O=0Y^_D!*0R z=2ZP93ZQ&^GCXK&DsxkTn%`5)PkjH2oxa!zSrFaWx5DivhM+aN;wgVGLV;4MZIWfYz2?McET;Tb__Qn-#2-<1I#`j3gh)1EUtJfH764hF#SPxoiytY?V$W<_lO*8N%jBllMp6B3pC2hJ*H{th~*77?F9LVzBFw$gDv7Yg-R4=t<};CW%VUs%vJK&y!IPzH3gFCV7M$ zOLrDsuSXHvo*)L4n4Bb<*`IPh&js|l$=>KG^TOLd_+#q@p zvjh5c0GcgX^GrB$Y)95PCAfNyGSYCpf-qhRgk18+%tgGk#T)26U2ayf!0C||`|#}W zA^=;!${p9#tyszoA49yr1UCVGe9l#_w@T|oK_7~?U3 zl8?WeJ+-6)7g}3CJFmm5+f&7r`<|-+&3knR6g@dvUn`0mDXEjnN5McTY+W{P9G*-f zet7?B_T+Xsy$1WuC88I{typ|z^{am|X+5fVd00Fkgb|0keK&8SEDG+6Fbic4G8(?> zW3?F7s^c)`J!FGl;(TU&OcMq6)7Jlw!)!ok(F>2Z7@ja8B;8fCCNP0ce*!)erLz$B z@T)vwJ?xy-P9UFZ*6uPVJU=`)C{EN4qFY~Qyg1z@cnr*nCl+JQ+d)Z^HJ3!Gk)1Hl z%h7;H_^!0+1FSUnj=Hz(RxCu(EEo!VK9i)0SM3yd{dJWRvlGz1AV^UZKFjJ33B*Ee zT9)w7(I5O{W5kMZ6bB$}J;EIsy^jzb@k1u*?)JY?x|_3mbuS1?Plzq_LoO%)9P;W* zE#~b$EIB(ZYzTDblOe6166dD$f4B5y$8iTW?}6=uh@Lp+p-Q(+GdK*3OA7ja@d_VQ zjT6P5Xo&A(&ZSb|slk^L2%yY`IgR6rq$9@s8TAmkG8JPoZM^i$ZyUOBxZg3uxF>#+ zIzyS|ZKI49l6W>Z z6@(uYcYiW}+uX-mI0*81`Gdrp?5WE@g#P>dl05zBYV_3!f|(DTQ4d4_)x*QXf;1fM z{jT_xVN@_-n@w15X^#80ZfLif0-Kt9P7x=XQH`#GjMt^ZbHZ&WJLI5uEG5GZ**r54 z69AP<3i~I0FuEJP8^Jso>cAg$kIrjk5Ko6ere{qv%WX&TC+JHO2v;4r-pGf){~Hc# z1}QCUtXod|nO-G+3T>j)59A`ON*z}$O)ptt=1Bw|yQjMP@by)Bh^VUm!eRPjJ5<*G zemrfSpHmyd3kT+@J83OJZZ->LMcYoMWqfX25dj(Hu7EvO$eAZCXeI(+v+Zls zxrFx2Eo)_6eZLl8AOFNJnn154nh0vxdWtHEa*lg6Zxx({d}4^R85bg;Cnqg#y;%VQ zqb+*#m*ki@cDklbKQ%ML>BguXeofI&3`c#6tgrX;ZEWv&w%@amal%asDDyWC z>%Q6(t-9RW0@d)<+D7+e&I+(aHNQtU{M_AQF zp=eYl#IWlR8vKb~EyuR&t$b=_32>%R<5?kNVxa)QuU};96dX)8$|6v08VP6bhb+Aw7 z%{v8oq4V1~D%w$81zGH;@YbCB^5^lHWc890=8fnN9jI-KHsw9(;LOf0T#bf`yx&wE zXWaI-P7m3o8e{XR#Uz~3y1&CFjJ4#g1jtcnyLMU{j58l-~Of|kJn4g6QtV| z?mmFu@8|tifn84D=aj!rvCjb+%|(p<#kxI-rdxYwS5uz4`N!aL{KMh;i^2==KgQAT zxgO>ECV031mgMpOcg8VL5t0`bR?z%5lBu$S&AL2d=XELOUi3$Q8hm$FK`Cc3dGQZi zZDFcMYY$Qc{mY-5iD-U+`nTk@Q!&Or9M_$hUND{}M4qNA*IoV2!{G4Z05sgjX0Yyl zmi46qQq~wO(J*=vBL_TF3d`{D4SvF2n1htDYuC!YiC@;qCmo3vE zpa$_mSxF$xFXVN&wr$Jv|4#FfI00)iFps;yao^v+19z@H?$i@OIVl9ot2Kc0{|wph zt~FtH{>#mIksCBZ-8lI;C^AzB0vi8jOn*nVc)vRa4&g(_7w=Lc-W3_G*o z)Jlzz56?h7$uWODmZLi;smTKVmfI$0jI`a(u^VB4VI7QXFQbS*T?6e`EQ-JUaO1o0 zBgH=*x$$S%RnIq0WdCha%>7?rMNwHnTuSyIbZJ0S$6=it*usKcy369E7x5 zF<8H#z-$HvvD2h;&!z}Nn0R6aD~N{vd#;*h)-Ur(ngtt2>^*D{gf3C7vMNOxN3dXZ zNcoh;(47a37>Y^j5^SvZp>gTG56kV)#c~=)Y>m^#E=L#9jX2=eu27EyEQ2I${YWcx z0;qt!Y^3|Of-qbRVWX*)PyP#75|idOIM_b%WpGb5sl->#UhS4X9v&zkmt4P`!m)zN zD$sXzE1BIE&UP?WuaYq59q23Jz$M`^OlVEZh9hzWr8=ng5=GX87@7Kix6c-V>^1GvgH?05>o^(`O=>LYT? zoUYi@*_u$zLAZDrbA7&`ywh1x29{KhYXYY`iZ<%KYV@=N5V>2~9H=<1xh5HO(cXkMWf&HcS zRI|(9t2W#o6&+jT?A z;6OB&`x50BR6w&`RymJ;F<>0TA)FQ;ug)@2$%t1u@Jj(T#|~J+x>S(?u99C1Mfmx{ zDQcKcN5AMSIXQ2D3zfG556!Y7{i{tx*jp>;evw1Rd?01@DgB|J_06wFaTr2+-*PV+h-rnX=#UYVU@tEO`f} zy|NFI=EFc1SVXPZ?1|@#q+?9e9ZBZpu*|!?tE8v{I}+%-45w8(oX+&xeQp98Q$VFZ zJ=%(VTY$0^veGu7^T!xMyGb%v6waA&=A)=UUi-2vyJ!rFao21Ah+f_{zu?Jllxu%h z;0^aww3&Za4CpHTX{&2FXh7<7bJY?iX{Ze-aai(R>(T455BiAl_Ql~NDhrWi|Z-P+I^&I1$+DI8uR9f zrTcY4r$_rCVbv4(hPZSz-adsIcmo9l1GxbH?4I5AA_ITEw?{kYm-*?M?GCdG@f2Za z5)Pd$cPpT-1L4&kNarg~R7$=aALMSqi;m+C4JdDz9+*`-#e*PYax}i|!eh+fRz(ry}_zT;2f)AH>Yt-iwQa zhkY&Oqus~OuaEG$bjRmuA4`3&d@m^wNF2D=@Oa4d(ggTX&$ThIice8rjF4+V(Xn+J zzDuQt1(!HNR8Fp9IF9YQXP)Mlb%q6>6#T0N3gxt9Uqjs4FBUc9-RuKeSb8O1-YlC6N?0fo~!I8+wAnZ+($Lerd~oH6dS z7tv2G5U!u5J9k$7>i{1S?^1(*G#dT?FSGye)ZqVS z6#p*<>Z>B8u+NLgx7=;~BOPIZJmOm2N+D1)G=Vx5MwsSoE!kCd8D$kkx^eBRt3C(F zOe0IgG~Qy%C)bNVf&x8QCtPhO9Jf!~r~Qz_({Pe8>YSNuH>T>JPDTC4_OMVNK^l$j+X- z^-!hM5e2ra)Wx-{>*{pJq&#EyYV!sk+%!f%y?hNi&-i}bNq@7}^xD@3$7EOkGuq0U z>!5Eo8qO#f{SP)e^MgOzRGii8=f(I-*Yi4cqOW3iNR2%{VSD+mA}&+VNwte<6b>yx zr-_|FvfRjhd62;hO^G_Zn`kX;E?A35Kpz)GjwtD}aytqmuJE?h)apJX#HC%+Wn#9^ zvwe>gT!JdKLes9}49G_IR^7Z4Hlu~A%27tXag-erdpgmX^`nwnFLty=-f+QA7QZXJ z!Q7v6?ax~SwM7OKMtN{tm-FsrUDBqXkUadq$<8G0flw4-((py0Iuq|2?Zq8S&~M%! zJL4n1(wuI2{>f-z*F&xmzbk)?|F-h?-()|w4i;AS2LI?(qIa~n00I*H>W);Dmw#eHkYUe58Y~*Mu_ub%R3j`$Bu{brr zT}%7UD#QIXsfYI zzn)nzJ#Q(OFL`3W@UUJPUKySkKDT+yZZ}IGyE&%r-0g~{3Mz2Z z$N-4+9x06NO8o|4-U1WWp)!$m=`+Lnj7!-Dj2~imUV1decbB(o&8J$9yyHmq+#zOG zcL+oKhCl}<$N^cN;MT>O%wzjhKi66L2TRjx#1OmrChwGcY(aQtPjq3zP zG>j2{^Z^LZc3_gShTARREL8bword&yHwZ z`1!#-4`}=WJ5^j1K(y@pE2%$bZ3Nm}!)fhk=buzr(G_8LI-gpORf9?3F5wQyF^i{A zFpK_xTxqjk>s~#hcT?&18qh}j1;BYQihe&l(D@nJ)~O-%rNV_q1=84AJL&2;%h`!J zEnfwDhb?)ds!-vvUWgoZWJVYrMmTEa=m?4%jUOvMJPoy941AAbYdYX9OjouQOyiHI z(9t!K;{BSLpgQ~$lR65iEH!ufFxjF^=KC0TMxT88L7O$zO4%^Y0R<;r1pb;~bLd2E8BV&kz;?yQ4}u(!ELP;o zWRfhVs5c~b@tbB!+&8Q#7@ojtP6VZR0Q^6}-VM`&Auc;LtbU%f=~tYz3oeRK;Mk%! zNvFsY1o0+Sntz+G6L%K%-FIszpE-EskHC+oJaDfD=0T28bU7YK%@Oz%D4-hm+p)QI z0}{nJ--S3ijq+IES4!!#oC9yQk2DAxNc8cj{6|P4lZ!Yz$2w3dkgWm^ zLiZ^VD8?T1w3{8mV5Vjf{|3h=2o6==K0n|0TF~5W(|EXRWzKy*H^g11$3f$x zFiJ0XLyS7*8Pjb^ygp#1esVgSVk_h;agwzO>MtHG4qg|;xsk@j^PRQn@%@kzmuyxm zpJ#bur~DyB#AUU%Fa} zT-=j#zIZy82yOS6Z_Eesl&XU`#w3DtZqg}2CZ2@|ztTP-BR8r%ap#dgw6&CUG+{vfBo3WsH&6V81%eWX!AUBX zZ2l|Dm20{>VIU4ju>}TV=P)nguM(M~Y$GAo-o#x7_~bBKwe|%2$J^L-N<1D~5L6KC zpxH3p1`fYZ@r^!0ZK&uXkviH)@qI!LAb;Tsh?5X5niM}(E)U49w|3d!Y+0#E70A8X zl@i!vxJXWrmnv7IP23j#fyHi9Ql$|%^>tW$(^l2CK&gsG!}rOJj~oQ0H#tkFa7*A$ zdMap6?EPK)s_95BUqwgot2Ac4rU^_VNGAcoK035hCq#iJ!1M^dZwKpH!~az-Fo#bV zGNw1m5N~vtt)4QVC8Sm@p8KzW5l{_u*my^;!yi^W-I-al5-59tMZP9O;9E$HT^v0f zY?X@BO7fDV>9hmnNjB5UX#x1ovPeK=)x^o#3@|I9Wq|!wRJ-JAA zQ9I<^AAjpYiRnL*+H{uA4xamyG?adi6z3ZNkLr7{3`||G%1-EF3y3km>TI^`eW(ID zyK`+NFGV6_7Uzfn+Oj8$P9}=_-36WWxGK`@70XYjhlFPJnKEs>nW&{Pz#MassW)*RyaPF*PCgBj2y08)Wt_Ox4Qf| z+SgT|9V&SEQ{Kks5Cp_lDLpDnD>CB##CJ@>r6YRt5k*2tEi&RC0Lv_T^xz&dhp!HY z7XgNxEm(wM+&}PT_xwS$I zv6i-yH5geBHNc$BS7OG;AFa|4WfdJ@9Bi(Z%g`D5_kM@SLy_LPb)5rG8kFlh*S$7sf0i}qng1Q^>--O zwvb128LE?bc#vyd(w}1sQnqb972_M$)w8QV${m^+XS@uX8ue1s@VljXvbI!}GMrZw z@@Q@}fBQktbJN328h_rq6U5A_4XdYO?ApqHwSw#B$Q_0=8bSg6PRVS;F(6x!3x1c&HSFJRnd$#!u7Nmq8E0 za^c+N)OD4DSt~i{EOG5?H2HzHDaRl=|B=liP`dXBPFr#CAVN|bYVTqjB-n; zwA0BO%)jzgdgsK8=s7y1E@wRhAB27qERY3XvGmgch1tTg9_WJ(MKb3(^W?05ioX znHwQS@{1#KN!F4ncKBXQW(SDobjTmtx||)Ap|~RFb&-rc%u(%C4hgfgD11q(Q3#qB zR*01{&NvXPuc75-&Npv3}b6=iV+y3w>komDrraIxu#4-I`K5SubuQxmmc7Qt*QMZ64 zi8o-E9jq4JjG1EPcRZtDRCU(zqw2bm>Qwc$@J5tKyu=0(y4kYZe8{@xO$wsF2t!;O zY1txCN83AcnQe{hCPzkA@8-s6lQcJ;h}1>a9-D%#5))ka1EQ36nFAL_dF>KoA6mC zlpGqEDrR^DIz6f@Kjzn^slOS++qhVWW_l9h4^lgALC9Di#6e>s`rpj<%rs*!EbnoIbo? z&~_>bIlEO~2|Q%UZ3N!w@+B+vz3vBusYb`|ZFs?9<`53pmwH;q8R^@52!xcjS^`)4 zpE5`4!lq?H2cVf+h zWH6P9A!0ai4`fUh))x?UzhR%d_-BNw1R4G-XcqprpBg;Tx>L6thLBOf4-;z)Az z2?S{oUjbS7)He7!fL!3o#id>27Qz)HrzoHmMSCdA>S8bqXoI@4>61_k;G*3@-@_8{ zBTzf0zJelKsOs&%m5tOj`f5!5L7|?LC+xWRk{|so+2#m1e6NqwRfpKJ>MycnH-R&6 zWCwr9{Al5(pf=ZsUW<@YzysWI_fv$&(F}7iP+RNnz>Yi!p72yi%zTzCYJj{37K~e*yagN z%s{(>Qj7t))Oa&n&$xjYW7Y6{Hw8a@GR8M`AeD3y#0YrJdIh_-uaoYNR|}^H7xC)} zdE5$W`~cnBVoQc*83>BQ4JF5cZpVuW&8(F0iYx3hBqcni#mD*jIUHdPK!l;+)u&2c z=n`ODd5^egvUh!owM01CV6*$feT0(7nnPlemDIc!MG!oBS$$dR@_Lk9gk{E8cAO># z0-_N88EcFgHlvWcCX4?ksV06-ecHo)KoE)fO$3Eo;j-ebPG|@@z zDX2Dy1Fx8tlH>&Zxh9ju&-8aAJ<12>$y7Z<@)v0NeQ*>6PG8ufaRMJFDye#Ph(Z;(Q+itSPPt9&8D{D6y`YB+7DPLG#Gpc6RqY#XXO`; zBY|JDl9tM7z!7;+1re?KND=21VF9ng$OWfyw)6ccmxdGC9vaDn2tvLjoaMpl-FwiV zpjZ}Ds!%w3ZdAPRC+phgA*D-4@8`OA-BMSM3}1*AR&``9zt@|YNla!e_d_YTI=O|? zsB>E#(0m{BzzSEbMP3j{3z3&QVLhimr^iv45g_W!k}{tJTI0z8#1Yw5tdm=b-L@usnluiCWxW zNFa41qB38fkwJ7Z{mlazDOM~9Lq9GC!Jn>4nsLL|#w1a@@2n7YN&v23%E}WKALV3E z)d6Wwo%5aL$nEFv*iMc#A5Jp+SP>Sdn1xDDg!yGEV;!t%J6_#dXk8&CS48Y@-Gw)ijwWo%OIvOY7wa!}9=*&Hg zZ=COM${xeh37tpD^sW9>2b+M2Ur~`8!R0`{-)o`1I54po952y2D$A?Y-GU&WGe}}i zciF@KN48rz4@~ngzd1~|&P^-<0g%USY*w)Y1+KvHe5VzazBfLk<5-5 z6aK`IreHLtiJHOR>x;-SH48~xd6V*XlabF^}DRfJ>wYxw2EwsaRwg-sG z@y|?+iz);I)q!mss!1qiyh3ov>I72P^JqjnuE(7DQcI!OX-V?K&p8HVMn9n0lcIq* zU=l-a9;GyHwW)BVv`jh$0R3BvN@Bi}O!$ru&vWJVBJp zvNnbnq+xy5fX3jJRK3Z18RBN^s$OX!CG8HDXW(vjqU33d8WmSnx=3{*3}c(B$uArh za39OXSz(XKai4epKl+$!JVy+tiZ$d%l)H<9Ly<$Wd#U=w z^?$~IrtL%tJr*?EhId1OBLMsvSjU3s?RCb0-oEf6(=;62=6oa+q4Ei~;s}`~4`}eo zrf1{f+B*o=Uhbmh85f23!BZ1&-;FwRU7kxF;O~Q`CU=yql9||D-sPbIo{K~i60W%E z))47`AOAWyO~Nls;YRHF<*4@<$8FqgN3a5-k$uU-doK4^>}nkEYkzCKSsI-2-!6W- z6H-l4jJeMk9tFI_OWio7*b1~&8rFSz4nH@?SQhY4@O@_AYRq-3!y>c7#loqF&}_`6@Q`MFO`ge-Xx zZ6mqd{dJFfyRYH@tS}b$28q<5EW=x=}x5ME}%Zsb@}Q?Gos{+H|02 zRe#ZY;7K}?P1uwT6v4p3kVfWLxq!~eWnW?vhWktIH5;(l0jCJDOXFW42>d(JxzuDf zchA!Y=F;4|A;ZHKjoB>8#W$07?yDkUbl5S^dFyWdZy9ukB5W&G2u&CD7ZN>7(g~yb zt%HCkixf<{uzs#3$eaXdT)!-vKoYG2WsAc2XQC2B4C_9CyDti1llh8pitWjZAss~C ziqnEK^tXmo=S5dmH0{-W!Ot1R>OF26k%N>NUk4EWE#F5LC7(xt{po{BdtA@RNx@C4 z#ybV!66NQ2nq(K4fCDt1AN&Jw_X| zVaXX!pF5s8wOGZ_OGOTSZ3C|=LesQuK$TBOtk~i?=s_GE?=YuHVW65vFU4P7>_H_( zO`~7=EgP&TuJzVLo1&)GH7YbE8Hups54_%5vWu7*Au6kPEGz@b7biVvyCR#+I>XDCoExPfz#`1I?IAbiIo0bNLvTw|58)MP zdyE2j=4u%yhtyEL$F%uqe$YM z=QNuVdk*^TykWAy(@RWA)PWDpM#x{W<8H3sF~AsN9h1kUpZK$_%H!~|`&q1pp`MWF zZ-Cp*cEAo#FU0Zw=lcf;Z8V%D>y0G>UDo6M-U8cuv24jW&-K_v^hD+ONEc? zR{+I?KcAN}cn{&|&{v5)4Jpg)I*=-Z{9F%hs$-fPQ>_MaC)otOR87%fee%Q6658IR z~$0tQk{iBg!*$lCMj#wQ!)4MmA1yxVr z&aCZR)e&1H$W)Loc*+wfF7W9hl=D&p4%r_$+*AEX!Fd3jAzEQW^=Bn!luxFz%KmJO z(-yIJ>Wny>loek)x@FP5-}rT?U0tQ%{#R=?Oh&*PRYZazYD_6N>4jz2$nhX4*9{d% zS4wR4_3utcfzo@Yd zoykFgfO(si;seF)oR8tt!XxU(RkHF@|C(La z%CDQLvmshfiQm&850#6@rPOFS;r7{=aa+Mwo>uZp3&Z01AKkmGf*aiPrtKZaL3lHV zH$g)8w^3FtTd8|1uCbOKa5P{7Qvyjx{2+&4@zDt5p5NkuI+@5~X*ZTbU|mLexb*ky z!`ASsXSzJk%N>!nD=EnF4Pmxu?s3DmN;lVt-cGfzU)){gQrKcS5guktMhF18GxzXM zRJTDUM?W%xUj#oN6aY

*0(3MUcHZL4!a^XYd}4UD#4UX4+`Q-og%VMMAJM`?8B* z4#bqqp4M;qi_+&%yfGz#>LOL633!aHkIA|`K8ZY5tl3qyqtbCcfy5ox>m_m8Dmo-N zF^?wMvBt5O*f&U83$Y_0zYJl_SYkv;C8adr2S;T$C%%)x_GhLIqs|IJ2v``GF4?av zN@X7<^kC15%i9ot3Z-z)>)4|b&oobQ@v|KLG*S{b>GteWlrz}&3Nj2FuFRNMM1P>A zQ8oR$nR|iXfQqZ4(pL>Xp74$w>}F4sn}=xq&XSxb$6GcmssIAs;RCSTZo|l<)MjeF zTgiZ>79Ax^Ut0x-_0Jp5i1g5uBTUlVXNsA?e6YE72@I4)O$B3Mkw=!3k!AZ4UUSf6 zj0#OD9fO}rQMjP_UT>Qu@gh!Qw_H;K_=fo#Kg=Z0 z+LsjObp?9KHJ<(qBipW8Dq5xS0BFa@@x5w`8yybg@mpd|Kf6VQTk$S3tM-n;8!_Y z$zWwT*F5lPreL2JzvReF;vh>=xf?rc@m<`yT%Emyz<3BF{QR2a>W8X6w^xMkEtEPx zZLm|$_Uz^>0ZDm*+@N^28T~PTI_Ew=!^nJx(rMibT_W&eYB+LOa7lxV2XH)PWPEAr%St~1+f5Z;4?!`K&0{Vx{UY$~$cT-iZ zY=kMQ35di^S4XR#f9$HOr)|?j`19fMhMx&g;ct@YxJr_iQU#*`wZRt2Y`OW(7PtS z=zuVG5(8)l-_BqOAxWQ-aGh8?tTp5py;e@Qrpeil_v#||12}GU5(c}IP>|wWqSX_^_$>uzC<9Q= zDr@whF!yl;&yWH|1@`%4O0;6XKJ%Tl_Y0gkkw(-f8QDQ-17QJrv(USkm{Wvk^R&E$ zPS;CQak#a?>!j4$M0*onrTP*9u|D6wCXpTfOI$P1bySboIJftzH9n>c_+9+yY4f!> zlA&%TG_~$xuF)rWia`=<=|QooUGcb0kQ-U|A~7H2V?G>u@%vaaZC{i7UfCY@s)FGq zp)UB5851R$RHNjL{8z@|`>Kpk75lBN*qp5hYiJhm1%+{G75~2R^{7Hvuk#oJ9+QNs zp+l|_klX^TYXbCuHpU7?iebyX?7&ggaanib0pytLw_62@B6_ifbfLPRL2=jb_@12f zV<2BYoPC3JYzih%g^29E_Q7dpX;)c4(KfJAA*u2&T72%;W-gkZy=c@>g;ys2w07J_ zJMnIXQS>11k3DR%Hi}Y{tW0fNQBNCwA8=9~b*jND`b?^Lv4*b$Ns``ek?sp<9Yt?> z5DE9oe)F@&7{Laieb1D%tMGf}wmKrjvNkA*R~r8<0%c-J`7d`mE;&Y;9tD-}_XZiZ zC?lQ82hz#p-SHFMn&R*3&)lmM*K_;HR-eH$>`WL|CwmBM+66rmF$JdY2+R9G@l0Mk zo}tmVZxl00MBV%d$w|Im=GYF!RT59b9gvi)QMbE_RSmgE?;-MQMBWDF5C-paIb5+BT4e~dVI+?U>>eE7b8c0!=>&-{^9YpsAb8EZy|db zoe}=)1|iNhdE?TWCLSaJyidZk^?N!^0h6@#c4tEb?B;fJt-&TKfDQ^RNa5?mKcR+? z0UyegVm6bqpTM(X9)!ED3K6do#M{M&6C>t;M*t*VTYhq0&Igu0qjv93qxkU^Hz?KF z{yu1{HSdbwkqsdIUzQ1r%AIwqg$orOaPAIJvH~kl%JP0bD#vY?hj%Mj>mvPIBU8CrxOC&W57fw=O-8OzZO>Z}T-gz|IG#C-177vX#i`IgcqfmC z>pY{P91X@vO2Zahown@6dsJH}i3&rLpO3;WkmO&sx?uj8F>i5!)2A{g%+UWN92i_n z3IQ}pg7$nRUu2oX@!M+CS*d#HPZh?}bq71Fp|pC%rt0bZD8=qe`kYiq*|QZ8oGXfa z?f7??i#pd)+uyNi^am8R%Q)0*+;P3a6^g(lby(?!?vw9+?1-uvkb+LIfOgg#EQ+G# zyk7kyOKP!O(sSdJI2M^%>Og(trfpFB&lutmBc%5$lXfzI#K8s=&gpR91a(6$9-?;f zW5o>4?<~va($603Z%r_|kkI+^29hh8*D@Srx@6OrqS2}wi~Z4A5VW|*^)tu5<2ayr z`~X4azLtXlPVcWsPZko-CvT80!h*btrZSrqISNqmOURA{#hs5Q!5pm#@dy8fy6IOtB7bsImTODpyS~4zll+MK4Fh+sE(>K1$brw z&!@!vQh&z-!kd^hJ{`Yz9zRFU2_xZJV01dQeMA%b@lMBn=#|0X0*Rv8q@maXsJ%+f zhmfQi3&%j|t+SaQc@z~e_6_i5^Ps3~f7qAS;+q~k1Oid!NxB0r?FJLgkglaj+XcxB zF_1M$qbiuJJF0ke>(qFtU!c#;s5E%7YeK9*2%BicZ&Br#S;^R9JwxP<6WmE<6$ zV!xkAzzcD?zdYJaV-^%%dB4^m_^Rdmi*;dKh(AJS_$d({3gn8r<@#6v3VE-52736a zWqcrFgAs1^fbcR5b>}lX>A0yN_Ymv{axNx5G00+9!#C{+6I2!uT9ql~8uavN$muz>TiDSdHzq)^`^B0Uq7|`{Y^3jiCsg<{(N@}>l+5^+1Vb%~kd98;TSC6>mixUNFIHI2u8DBX#vU-0JkqAdw;s;QVj zt^ufTKRwkR{`Vx8JAE2Oavj>UlHQktr)66pUNyhM`BXC-?u(;8Sb4+`v}8JkjWZ8R ze9yR{ATff3G}m~{j;MvK0ik9jBwXn7JoD@iQH7X^b+wq)$F5Xno7wj%+U!c9p4h=< zc(fmhCMNXMENyLvoC7MEfxywDa*?CoJH2l`Y^|#{pUKK>=(Yz6dB40EoGiUr_!G1e|ylMAeRzo?f^{= zV}`Ho`yM<+KR})d#0u(W-JD3*MzVh!t*(C2O*8mS8g-Er`?!sSUQ~p%IB0U_sb4^@ z=i=lUny-c+%D(yLX*pb71V_fM6;pm;l4w_Su3l#aV!3nS+a}a1Hs%aRL2xu&us-z# z8kzS210JRe#@K+WVexsTvUdPM4`;3ib~Kf(1|Qr+en(IT4d|6a`j5}Fq#3;0iGV_} za#GPD+-;O#iZXK{T7YEjZ&sI23{v1PG@qFK$aWjNu39+;5eG_`OmKKXV%hDJ`C!;- zh$rBbMiGxHYHxg$6BWq|&wwlkavboV+Fb&0lyrd;-8VV%ysS49P?Lv$$VU$%j-auH zjgkKS<$xk{Q(R41EB25ZAI0k-j3JeFw2sr@LAJ))juzCVT%jp`S=B7T?)sj327RP9xN?efL{PL2JuGoWde z=&nestijy6|H=XTg@SNieSpvoGA3pf`Z1%Kf!E7dLEDwo&!M@ z^ED}ENHpJs1$9PO^HGJ_-00jSuLg?)DiMaM5h=>CU;cFTEA7ykWj>DQK;7hJfj}9X zQER`XL-)InNJ8$$R?9~lzc|TP8519*1{`+l#Q_n#p^QA)H9)G2Ueav|tE5OW zRvFF9_q;Btq0^#aoEczM8CwpBI6$)lzC(T_UkxMU z)g~SM+t~sQbZ)Hz)eiRlYIV}JKVr6-5Y4+q?W-ZMN)s9j(ET?{&*g_C{ zSFlQki@2MWql9&L?5rg6ZO`V@qvfMo$wYj)2#6p-A9eoX-*PP0zZE%IeG$>xt3loC zU2s4J4BCgKD8I8p0XkWF1zJ6os5fUbFP*&HTTXiMlptd)stQ>H9c1gJe2%C1Q-6xv zfi1b$Icx&-nWeQ&I03Q}I(Av?8|dy}Csdka%a^jMfc$Gx#(y~n6?~-gYJ#hDW4gOQ zBY}f;ffwB@x$P8Wu|kh?YsM7A3GsL;Yzprg>m)(m`n7(O3e*_$DD$XL0_2?L6K4|C zz+jY}20=Em74=UBtof!+s=_2xWkbrz+4_~{xv6OAzrC6ThZRx9_23M`6>Cc-;7`eL z?1N9|=c(1g-h_==BkR$A>*EV!a)2UUAo8;+bgi^(V11RE;456ylk+Rs0+?V0la{?1 zpg_f?-!a1J((YWzb@$z4KtBl0chx8Kkz6zXf{gNu@5X|-H2cIzKBhoT(c+bE3llNp zul|fOBI4)+G_e*y!8j1|n5Oqr(Y#2M`Y$TzL)go8G}cow-!tDkd$WxKtEz+HvRps& zWJWVBifdUDjVn5ETH+z@fz+U+0x{ueTDsAQcX->*d)AcP7~Tj9ATQ4e82yWXfYQuV zRhvHmFKGpqmFv7JL1%jK)O0Okoq9O;ei8V{*MR*oT=Fewuwd!caR?zP>(B`VA|eAa zTH3?7u_FA)uT9!O|C558pU4MQLIEMATUdM!G#0^%UA5qR*#3;pR;fB8YBy$nhbt)I zcmO!!dsM%KD+ir0Xyz2#_zH9cgtz%_v9hyMcb@rfqmoWU{;#Xf3Tp)9 zp=)bBv?vr0Rk)i9)Ml}HAjLAa?DskptX_uN?$r0UhrGQedcE7Z#q>&F#G>XAAETo6 zHG_}Ql?-mnL5k9Jh|weX)?5}7O`q@lY>;FI?P{la0S&s%oUws}g=maHe8Fh{V}VBG z)!r^lst@SYwM)-=k>#7t2}huN3cT-E2POXC-V}kcIJuTV8-Yp4>&d9)*0k>ShE*R} zTJYeW418sGg-VUfZVWW&4jpCN*HxE-y%xB_Mf$5#f&L2R_!{)-;gPY%iJcWNdBrjW zbYq~|ulRvzS<9!*jij$Rj?r`YC-PwLyG+YC$dQTZ{5C(3JA$6AD|}u)u>%2%+I?FR z;7FYI#52=5pkH_*dn2jVL9?YdW;NT`!X-nLED$@qE}OGbZ{#+=uLt(sV;qo z#>&(@@%g$i&$HERY3QFV$?0HFA(v?ndnhf802;xw7bm$rQBZ~2jlelQgr^Y^sB6+T z|4w<9l$Ed&{TIC{h$JEoh)MEWUO|hmSAT_w+t&~DH=Lpx#N}OX;g8;|#nE#J>R6fY z#I*`It`f-{EG4{)wKPp3zRqjV#pn$%7HFLi(-4SesI=xmf7C~tS{H(A*#(I_XP}IQ zye{^=uyO4SOtw?>*bF+*Fyt=)i=CyIvbBKpiC5IIzXV18D}J6MmD%UvH6?HkG9PksOncw&u5gL7SSwHqTA1#t84Y$@Ma8 z2JnugMiP_>5Y3%130O|_O?-W zgzz$P(DV&~qvL|iT-DRBn)1l<;(X|R^MD}gl=}bz%M!CEhYcTTaR&g~*NbEyBF#3p zTASn-_l&S5KW)jp7tl+|^Brm%7UOo6BL|HAC3e$A;;8mm^i5qK{D%#`w$FkHx+y2nYh4vKjtefE`rpEa zSUD6e$3M`|1$w|;J2TM)6y`Q%b973GjnOL~n%uYqB5sppDR0IO%Q~=JG1=5u(>UC0P{ijyyCX67(6caA28HT2pnhvc z1eq}hEB8cPqnlJ85e2gG%!qG3-x}UaCUv?nfo%G0oUr(vtOGTI-{ysX9Q97XLbwK? zp!T=7cFG^mS9GSh=!G51<%xAsSQZ=|Uz&ZZ3sN|1(u36KFNnQY$`<&cnhV%YW#~~#chAYx1aHXvEs7>)O`YIw2t2ThnE1^=YkHk zxw%z{%rXilP^oU+5&(whL+mUk&~3fv#Pq&+AH5No=prujAa8fVpbvJR&_1S)ws$^wk-w~dbqh&jcqNf^ebGK&rq)(KeLjX# zRJN3YUjQw`FYMcPpmSB1a;a+FNMPTu(EgB&iY?w+nA{^+8sO5~`Gd_|^~yUS5>sVt z%)mlXa{qmX(4CT10TR^UL-byKw2ik3~NZR5c-?-2$GtgBgkOA)r}x z^}y-^C^CH=GHQolN`*69huv&dLoyjQCE0wZdms61peE_i!#E}qMuMvM149`lcoqP~ zqRjBA1$23TwP!#yDr=$2yZ-^sEwo%lT6lUC$_dErBX|Em_X#BDGd;u*_KJF1MH4eY zllko7Vg(r^V2+akV(J^Va+^_;MV0SE_Rlg^wSaas;UE$Wf8{zwwzC($&+4b%00^cc z?sPf|s)`GT5v(_Sbd~_3e3;rxi!o7|Xn;nQ4v3e%!Df4O>ap*txPSUo4>>X>8#44H z)xjO{9-G@tH5^$Cfbb3SlVN969xZo)q3e1imh_|dv& z1Onzy3sOhd1<3*+p#Hdhi5n+^54?5xk_>vV7@#nGzqz^Itipx_zQl-YN7W1MJoae1 zlfk$F1*jMJFH#YxiLt_@L<3kfZ_ub-! zp!;paAC*UMe|OC=Tq-k9K*O6666ozszbzb;z&2=tSb)yS6k_) zWzJ_az+S+ak5e@m3ng;}vU*NlcgJU#{O>O=XO0Lcq?yZ7_G}eVVMDu&2+qU-xjv{s zH%7dftqcC>j;> z!6qIz(;l`4IKJ&-sm_^(`Eecu*~1@@!?vQvVsEBPzbj8-CaEp#W?HFc*T!6i7w>v(fu>kY&6jTDHA>>Si6@fVPX_6DFQ zer%Ar+pNf**xTCM`r&(|CSW5E)!_She_o6chlFd@gP&0*%vTKQGiekbNch`+C)L6# z*`Q^|vVDqr`ft-JD|ol*&iTIn7h}hr_qAJ==i`Na@T{As-St|JFI-q>t@p<{``nU# zpo2Ll>^dwpQ*uX7Ayg+cKR}JlrKuH%b{U#aR32=K%n5p#KtKc0zrn;+mT*+hRk>BB zbRATL;mV;`f^Ac9(b&p*-qQ<;Z(e#*(&E?YU1fgRK+bqr#NX@g+mNX#+2)Q>i1@8P zizlCgq30^vQa%Ru9>G>Xn`T<--EI>?;}QjUZp|iw4h`sGlfEgspOJK+C}!2_*R+W% zKa!`;0iSGn9n9_`QEzqXZ(EEkpUvN{{K`A!OEX?AZ_hxt_YK4fP(@#2#U|2h|1x~2 zp3+UM$s~(83@62GURy!8$tfWKgXO#bu5d1kDCfZ10|QNIx4$ftct>=%PMYT8VIu&q z&{rEaXkh;!vRlYsPmllu=9ksXz8ig6vj^>+1_P9aZ1dJ6e)J=t$DOg9QyoF-P?_qY z^6leI%DI3d8o`m)uN@R!-%8i6N31s-Z;Un{fZh3ww`HV$$K=_5+o-`k5#S8FPVKL+vqLhQpMCj*J0Eb^lNBas61N8EIl@{#26g21oTW)g7n=p1 z)gE4Mr1LMa3nVeOtb0w4vO3fVx9#{$G1z-h@e?ws;&{K2H|Rm}9wvLcobi?b4f@Vv zh5BfKRohESH-HcZ#(_agi)L++?8e~^SwO5FcOyi58{4fZUV*x`8P zc#lnV@dd`{XbfG*(+xTN=CdQh1(SnPyB#$Thx5h7P+GthMHKi5j6$HSjw2vlsMJnu z295lJhNsb>-m%k|7PTK`S&cKuEm9343Y@Vy=_~a>zWZ)V<-JqP)rR#T@>Bu3m*=@_ zyt^G6?Rz?L{W~IC*ovEbZRq^ByNVbBik5wEa5cPAlmdIapwI;>_}T#s`uNrX^3c^# z7J5DK(-8zutwC87w>Ho!$)Y+V4Sqx9*H{g3)Qazu-K__DAwBH-Dk~uCdYkkJx|v?3 z)HhTkXOi#+$|5T=V$=Y&b(GOTCDK6vu)+5ky$z60(=(eqM<9u&6F$Ep1wh;ClOAwp z02~x={`;uWJ8yMb5e^@8YnVW9_t>q1nDaho4Ea`)?%DDX1`#vX?Lz=t6P*j!QTeZE zXkEnxNr;s0+v!K^wxC_R7?WfH!!DQKn90Esx9bo1FBP*r~rz!vF1D56kb z%#mhLX*;K@p?Ho8g;&u3(C#zXZJ4b;&$9T*6Mo|Y&71SQcUn0zSg<$ic zfD13Id912F-}-kMX+5?|8|GevN2dA)`X`D}`+|MiRBb=qoAOQ-VbEaKlX>Ws#Dm58>CEW$fBPy&?20 zQjz0vjzXT}hMSkjAJF+JVLksMBe+AskHWsjqiK2YXka@EWRlF)aYeG$7ncD$!omHr z__wZs&Y?E0b)8V;x>ZmcP}lDr+}*7~g45zw+`YIImllFUf#M;!yOiR^2~ebHafcT7 zQrxw;1Oj~1=RN1K_x`&%S9@m7BzyMK-+v`Nh1|@mM(yChPdcf{^`bQv*lg7)-AO?b z>S(f=s+oz;6Zk~!QQgk2cH?$T z-LLGFo?ovMCPN<6fcsxo5VXgN9+OTsYhPW78RHmU7-vZ^J247wk-Her+Xv%+n`R=* z8(g10eds*uf?KcZqR3l-_E+h0q(|SSFZkvaxKean!dB>#FVU7d$LWP{m^BdMM;G-S zy4I7Qd3QW-Yx?3Cp5DB8E ztLe%F_saBqj+trw0Q#L@WvQXGIn{O;TuRAyrl8K)mITwB?DU7unw!adsy|-NK$6cq z2A#ZGjDcX6fWslaY>Be~D;iW`dnN1DR7csGXtaGQ3i3nWcw4NS zM>HahyFs}x!DbvJCn%oUk6LpHuOh^>(T5)UWbJXk9KV-4fUSL3;5upEBcMeR;!eKW zSHTO*46+ouvFzkPpOpi~lB`KXLxLYanewn6sL^_gQGANQ*hgY?oLq|&FQ}%u3JD3T z(G0wF{%wh~h+x6)^$jWv-x!3#ddS@eUVE&0PVL4J>pU}sj(e6U=QF2Bm%LZaR%(lm zJ9GNuXFO&t&h|1%MnY5lJia6_7spSNXO_Mt$^gW*BldO`@Qx}qu2e>tKpX>A?t{v{8sTaP%6j~e>T8o=cv;(Gz7v5RL!Hu-I101kuM6&e3Po^~RiaDoi- zArfKSML<2^5%C}t#clFo4b5M5-Luq~l9!vG7gn0QuM&tQlS9SD!5(D7>qTN@#=dZw zyu^}|VbHVjCTP6lBGu~7gdlY|FL9qej!4Ii!3+0CX zq{?Mxj0Ei3y8x zG2Spt_e1+<$taJ$ZYZ@qwnUM^Q0r3R6mAT7$*wk8){r~}Ra;oA_So`md%qpZ3Hg`H zm2Ql6a{epepPoj;>xIQ1%9V1f5i2FfFXswO7V?aYe_KZO@i4JXnp>eFeGk@RO{RX+ zSj>TLTq_*;lEiD0I%&#fAvxr${`rU2>-aDAp8|lndeG9d9r=KYvytQo?Kjf0!9t~g zT~l=Id$&khlWO1O1CDq2n}q(gd@nIPy!ZlL>>h#xltle@8);lmY}h7=@grPhHy-HO z!AtHAH*nP9Gv+$akFy!;u86~qUt(8H#LeGXep0Z_aQ%VfeY9;PW&ntZ`^wU*Fl&9F zv0RJdAhuY2$d*8Kmyc1aS#qm6o0RYVtl!rZQ?5;)AE-|b(EqlZH|swfqD|A|^4kLJ zr$FJ$2tIYEIZf5c3_OJ6|E?f?FchjxGCCz?`vx3XnmRsjJ?t$N=5AP7))8b%EO&;wNDW%PW^kIm3L^^|BjXp}L(_H^{H>l8PH z%c`=6Hw`DYnJ|FsrZ@2!WJ1Wiy#knrCQdLsG)6;zuv25jUIC_OzU=D!^P2B~_?|NRMVdirfv zZS`-sI{#t+dxRl|ssG!C8Y~|SP0E+&9ph8}udk9OB)nj9{9iUyVl-q~2=RcxPkOZf zzFm4avFd->2$e}OG2tqtGhCsh|LchiQuXNnuRkKVme38E2Sqv%P=kaEs1=CA&_Qm& zE#QY%E8gm3@L)(=0Zxf|wo3q2NbA_^!i`-gMT=|m0P^w)7o=L`?i99pvb98_#3}$j z(mr|tGIV$9cvc#40w_i~L+WM$bj;-|!5m*=Ldm=706zh7C}%{{I)Grn2c(von<6wQ z1E3S>tmf@n+yijV{~@e63`UCe))yd(M5`;j z1oF#B1Kxb{GHi-uT^Upsj6*{)p-A?rY`*IV42!IKK+;!JP;&T>k(C5 zeoGU0ywMnAslA-xv+^$#zK*OyNjKEsjDx=Y<{|DxQ0P z$bM%>Ds1F)on_Srgr(EWM7pPp|8 zLso7FqpKXNa8xyJ*%$y-W?lhSIkeveiMZZdMEuqfz-N5bFatOAezNAdbyc<4N;C)FkwhldlmuOnd+NZwH~ z{`yI1xLYd=>Gk*e>GjX9b=xaM`clN1FR>nno*sz`L^-13IGJsdb=a8qk5shqUh+{&_z#u^ z`Ue}+C>(uq4sI?1QKnxkNOeMQ?mLq(-6UHFTZKgST=g&HMwJVV%B|~O5$j7TAu6hp z5pFAoSX(k)EA^dGDf#>3n)2tHdr^*2^2rWtLAF?qbV&$8k4L-cd?ss6Wa=Y@Va+Nz zF1&vZaG_X$u-k-w36w>QYq>x>IV$QDUpNLNC_cA?9=RP&qsPH?-#c>}4`X~cb{rpr zsoCL+?P}igX#j4ScYDZKs?UM7iU4^|l$bDUV@iE8 z>!Pnw_Ja&s%yy(IiUvmO!PhF7L&g&CA1N&H1%|*dqaWptNgg()<)CxPz)5UkevUX0 z{rsQ`SzOD-dUw0q%!HRZb7Xi}Qa&EBDD17NMGpVd*7rijUfH=M2hKh2!fn>?mn7Ql z%8)2AsYpHBih%mCM*Jw0hqBu-zGsOlFX9tNSx6z?7rlQH(xM=;=gnS3o~;HKNJ>w$ z1q-j{Od!8F@&~SFr~T^SjQL}QNPgKxUp&`Xdy6}W*K%Xz`rX_zD;LHezeAS#ltW#a z>#*m25}6CRMx`39q_ne!{3p5PhI9GcQa3KWis^D&kBnggEV4lPH|4iSW5?jH1FwCp zCuC1C$|{6pPA`wO)Z)3Ewf1xt-mEPn zM(-f^f>U8}EP;9vacc4-1i}sd>{TC)xc9<95tm=NX~p)!QOAkl3_T$C0xGIU*SMk% zZJELg>5xj+!})v7-k(ngWOOh>(@>DiV7SpFZOTznxC8ZGG;05nh|2Lqg`^Dbs0`mw zm%Bu!>xF2f|7^K0sN&vB^`g(K((qsrqeGR!Z;L zCY;vNWk zh10m-C%bt#MvJ;IjHEbw*Pv}~6Y)D&O}m9_dM2%Te{ zmdnt6(<>oY;nZj%zSEsSdp$?j3%fEOl$x=`&^Z`R<1kJkJbJ~+);VK|Dr%q%5&yBOTrtHaJ6T0Trpri=o0q1Qlv+H!9VdfsBs?U?HF~K@ zWXEBbqs9^Budg2?X=W9lD)}CC3mU@3lu<~0;>s9pQ#SgwFscY~1WD;J9&{<{iWB>c z(-ENWhxU8Pk#Py_4f%BT_5RF6@az$f2rY;a5OGxtkC!C@;aUGK(dfwuF*&(o$97nw z(E^Hp+$ljh$kU}@;rFr^ax?0I7xQn z95M17j_B`k!h%PmB$Z(O3s|ElP{R=YILFgqRKf!I4%ansY$-05O^OJ>18c^FM{|6* z$oVfxNRPQliYyCpa`;~S?SJV-LVeUQgglw3@0)*9L9da|iDv1!bldjV2M~gB8RNWQ1hsbem~bUVbL`2Aw3F-5l5W((6D>l*<^(>P&NqDFvIGSH=#>UEA)h)tV3s_-tvnt0;wm3ouf zhhA}|!Ek@m2q8P4{b5p9K*j9YY(Rq5B$Z_Lmuad$OFCBgj5oop0Y;QfS(l`#cm1fV zyxL0dYSoi&vEeF7GmN>!|9p=GvbxTXcrdkKD~rcE&qPd~DbEI2QgDP-bNdu9T*>lh z6nQ-@D>l6B{C+#xPwTTV=>8zX93&ex^@yP_qOgOOU}D- zDswx!QTG0@&!fNk#92qJW>%fhThHIEEYp8;NeI8q-0QN!r(m&p??NTnSDf6Q*lKh2 zL-PgTXm-0q{rkvGW>6$YWFw?rtgBq~eP&mLuU%$WxM(b63{5mTNduoNUTPIzD@?Fm zQJmAFlz#aNkltunzbIwpE|jk(RK@YyHzPANQZ-F* zXK0!-b1hnxBz2#Un{a{Yk}DT5sfOpPp%BwryNtALuow4et7F7s|6a0K#sR_(d6lX4 z$u~B$c_d(5d6=vEQU0=*=pq$dYK+eDZ+SQBAZ5<{YHV(dLwFP)Ik<$yjc7aR?#4D{$o@mKVRtGtPJ1`2Ml4^e0@FTNVVNj18U45q*$j;0qrS2tav z1CT7HH1OJ&30~OX1!X1jGQ7+WLV@l&fdX>;$C-y*z9QV(0}{5=Kqp+g`>S9=c1x%Hk#@8aZ$D!u%I(f()o~8mgu*p8|T9v!rXCecr?D>Mb*E` zx1St&WKP#D&)m*X*iTPE8Il>8S9Z@OzOg$Zf`v1pg$J;ka|AK!C610vxOg$WCIV=# zUhjN?CFB@9OK6|3HYZ~IiZEuc0(~g+YqKS z7``0_ilni)&p&&YKd`tL`2#~`Li9R|SG!?e^CtHP!E@bY!%^!JD{*?%`k;nj-3H~` zs*Ofd=HP?52W9D11t`L(G7*MTgQq9_JQ&8zxE)#Qcr5%-r2ilv^7;*Pf~{^rxf)0b zq+)94uXicz=;dY>p#*8kdl~SpS6TcmNq8F^vmXYPlqZ00k5LO9km1 zJ^9v}JzxJANCoYu!5r>*4nnJSLq$|LSuY-(&|t>=8ieYnE09H4t@9wG)AO;W((5Rw z?7n-fY_%@0)ZH^dSlEASHzQjYh8Ya2pc?Bff6DxX?y#wLpHihimJ6mXF@%V`fsqT` zT3fAM?*`7_tAs;UBkCpy$3|md90u}kOz>Y=>)efI#Bypr?z(T(!EegA^*|yKwP`@d zYj-*=-XdQL6K=S7cuIsMj2h6K3AN4p7`9@=xRl^ zw3i=-Slny>QU1@qY@!Io-vaS0bKO@!@RzT`l^~;JL@p@@6jpB_adjIbPmEqRzGwgy zim~Och(7Nnj{~*vWT{slC)s3be zA;;0jvsRjU>3B_b`zb69A}Qcs+)G5X6Dgk@liPYBr!bPTM(~^I4M<9F*{By58W@8K zpGxfW!8IiiAeps!FK*ybIN=yn>7yN3v_>)~HICuFZY<2b>tP$P;&Y;Fr9rANtXY_8 zOI7^ONvw-WYAF1mhgF`EWO*fw4YJgLTcz<4Q?5QRLs0-rpOb;agju}qGvhNKVMw?- z-MU^`t>?43Fyv_Ru|jaK0zb&Hc+;-&)qP;nmD|h&J?iduWVTk;hW6u^rQR98c@5oH zTe8oTH{mn^j9L2V!h;Wpt^jenC5BMo!`8`zEPMLNze zQGzoRFMeE0r>dmLq%dZEpsw+{Nqw>u)Nj9%VFF_-&5Uw5{BBp$tc~B}s%q5aQw>of zoBA4?1Z>MQr}`&+SboFg$)XM{PyZ#UGuqoU(#r2Y~I!^RPw6=>a5oTi3biM;1fTE62Wijh=9e9m@G(a6F!u1pLI*@l+-=iuCb zOg`pKq0xXVbAZu>25>!KAds)#CuL&;(@ zE#hcH|AhN7NsTUF@-CQpC-QMGYfGGuGNjWDVd{bVN7_R=1#(aMk2;(H(1X(A4D1#Z zh7-*+YjFRK!;VRjwsKzTg_j#o4Y7YAzaL0yP}!Uado34TY#|FF9+Cga^RG2^w+mr` zFQqxdQ`O z7EuHiDR7-~R)3DM278-)XT|KtM5bsKV%u3SsHmQE@LHQOE}j7kTu9I{?W;2X<>qWm zaHBCWUh(#^dJ24lhT$ko{$TIb(AiZa5Bcu0 zX;Q>;qru$e!MXyk!r(8^BbpX!h-s_r(Fzh2i;_k$&7TB9IU8>sO$^z_KI z)hfpeCOh;qs-7QRaE07F2$zeY}PxDY4?F9*RbGWH%cgDh1-`@caheXg#}IW`-<|uZvBv~bAXHw*TwXF^&F$*i zGWU?mYj`z~#9&9o@LKjPY#n>5xiS==xNz@Qb89X&wsdXHxLPxYG}r~cEEEQRq+@|w z@u0+$9|=mE2cB@JmJaNsDV$C9hz1AIDc}AhsW5>^DFqXV8V0jT&b~3@^(KSR@5H1G z0f_B{I|XPDt^oCzA7!9snQ}JDFPz!1A`fb2Y$AbfxLCg1$p)*q4>|5LV7=#Z{qDpE z87!fMj#2D}{NbIabnvl?7NaIHin-tNcF@W_5iM$DSPQ;n`y;{g>JrMcAwk?4TXUna z^Ga<9qY<-|Dyi_x8)^)JmYhPKCyfk2r1tt^od3x)F66h8l8RQ(KL^u0ap$`=FttWE zxf%y(kwKDx@;gdo>UZ2)boTrdy41cvz;PR8SVyOf4^WyM#LR`7B4)pCAkGdWRg2kv z6O#Uy;<1ryp4y@!7d}SNS{@=Zw!CGMCLbZtSP5xpDPfhAQ4Wc%kQ(|~Ow2QAKpt|X zOGt>ym?-_d;1k=;E^^9d>IDxOy%L^kESBkZT^g%31=8H?Nk-nC2*_RbYhhzDqsE(^ zsQbl6`YJJ&VpfiC(!{d}6Qx{YP)elo3oPx6Brhrlv-_^jME_4hjCZ=wCz~KK-wWt; zhWa{JO8(c>o4mP)yQI4uHC^KX|H2FObi2RZaF?)v%5-GGE*>i>(c86oT1c4ipw^7Z z&D}Bdn;#HJoG+d)9Oe`SZG?8m)hf#Y`q?wDN75R9c7Bvg?mHWD06YB&eGxe~ZQkiMsFBGjHOE zN#QUj#&s~|F<1m5(Vrh#y!YlJz`FiLaCG5;W8Q7Ey7J_wulQi|%ghbNvm<{0* z!Oz(bEgn|!Q>^ACdambEpOZKaW|B7Se(n7cdqb!YbGzm39-(ee)pX|ast2+F%p zy3V?xI+u5pon_zJ4CIzZ71DXDB*?=8KgmW-=nkp3q}10IUMWXYBI8C|?616khaKSH zQ=qDiFVP)yUM`K08PG}Kb|>32$bo7eild`m6~ z%6EY112*M=VtG6yV)`wfJ31JPLbN#khbvbcrDsO%k=Nnm-T@2U&)0H~IF@o#S(UhJ zYv>m`#N)3WPSt%?|ISfVq|auZz5RxYI*t=EaACw#yR z@VP~89uNQtcidVUq8lq86=P2%Uwa7O-{q*7yis4Vw%oKw zz8$DcMF$p8m(Xfm;<#jk&f`U7qDeAe2H?Li{~d0qiObGKPF~%r3V*`ahOnFjej#uU ze68^|`VTn@_nQh?LPAt0AQJ!e-cG}+%Yk!{9v`xa{QBlRI``F=>dSN|+&xmN^nI7d zi~zoJ$<0cdZ~TtO4@%c|0Uj)}a~v%{pF={cLc<ZH#?=8qoZCto zU_XN(2T#ToL*9p*q^17>3oFzgUE&TRk9hM14Is(Jjgf|_ul!#s6pOSw%jj}-dK%R^ zcW4j@=k*#123TT1&=3NZCg@2lU=K>{J5VBP8sww~Fl{~npvNO{2wq5po0Lo&2|Rii zYkcBRM>@UE_THBmrupp+92!at9?oRI``%O&`zQB^eL}x#3Xk>8Lr+f+l1hq5fqWgp z?Np0Eg9cJ#N^nn#I;5`ee!A(fJ(~B{*W#sl9ZBEd%l-|znIvYl2oWN}ntb!9ZfNt5 zSeeSX4BJiJ@V}WXMoNvlF=5R~d&&K#Li?{LX^=?v>|RD_9Bm2BHYtHRngvl`xj2jH z6+tx1yx7Bzzv`zmR7rdt=v4bYC9a%7vY^|tv@}T^5M=gP!!$JXpej{Kigp-i&#Pb^ z0*v(Jac6YfTeV56T72Oz>|Y4}A`brTEIwVT5`FNZqZhSUmzyBqju3i4;?Ma0$99X` zIScIZRG2GA5-?Ypp)^4KXMeerJWIGLVxeZuYN1niU(%Ap@8j3R&J`LawtW&>GuUR{ zsl)c@Mu-H)c)|Nmyo=4haK6Gl`hUF>Z+{{%muM|a0pfDr%)q|9zTmf?kd)T_EM=ko z)>I{gMdANIR3spkp4@-0viHl1RQ0X5J}Qk2{VSrDhU(xA|JQb3sFibrFGA5%n=!WT zZB}eeFD?eg<0$tYKa9Z?5~+!=U%p8=9%$xJ1Pu;wdRy&$msqU-P^FWV*C6DMs*nkz?BuSm)WlFVOo`La{XNAc;|d@62ycsH?!Y4~V;P`$9a_A`7>dsY_5f zZ~2!r#JoKqo*ECsvM+^*%_K{kZEM`_aP0DTOVInajfXby*qpv>GSJs|aQvsP!(Yby zt}@uovZTuq?H#wqFY6-Q#|tJE(iB*QIAKiLZrNXe93{}&BM=RBuXu{0JHuD zW~+zqloJFRV+LQ>B42VUa&|fp03~G-tntn_=UIx?J}tpcvxXYdGew-q(67LSY!cx( z+kEFD{#^@KZAG?APp1BRT8R+Z0DedAm2RrO@*nhhRqh&jM3R!ZsV+Dkjb@HZ6;|4L^ z6rt_}3(#zXOyMTHsUV~AyM;5b7(L9y(o>nFn!pv5!yYP}8{B=}oN;6tQ4?E8_!v80 z{82Qq$ zIh`R@p-m~U9{aPud`}_ZsyFuTNiFaX z$HcFIyPjuumt2dYB1z+F&ANPpPX_7v9XUcJ!$0|(%yV^W{*nbB@?dtpeaA}w{#pj> z4Eh7a-bAkN)-xG97njM+LYjmN{*-XSF32EN?S;frjEUS7(tU{cz#GtOjT(55dW-M1 zz8XZmy81h0dn8o>jM>xEBNDJoapLb&;QWnxf31j@V}AcjR>u_AtK#A@T^tj`1ljO< zg^f%&hLf2n^;^^FqNV41tLF@ryLRvsW+PRqFq|&?efd}Ln10-xaz2njiWjSXmKhTh z`6d#Y1boQWHK9(%g2Q2{J)Zr6ok{sEnM%4ltRmwY-k_?cOzOe3ij8P~!^y)1_cJlc zwZqppLANhxn&@O^?$q661xjCl75^s0W5p&`GEGU9U z^tU>{k+0Jbrr<;`R16QQ0&oDVKejK>bdz3xmIM4iS$Dr-1?(ZIqt&W+hxjgvm$q^;Hl>Wx)Gm+SBj{KfvDzB3?rvec+ zHg<^3F3|n7mv4JC`E5m)%EQ!I&@&QE2CkuqnrSsfmrW3rIesCcf7M^dfgBYTNlcC& zDGBHyZ|}%M2z|9%ciqdxZCC1va)#f%2l$8tMzdrEqyST3%mC#ySVjLZPB$uGcY99^ z5KDPY(S6Ocv}2gvqsRH;6UxImru4kev0Otoz6Qio9~&Mm)IbEI7l21k+I&c*PogJ0 z%c{e-VXcvtmL{a0tB$MK`p#fK4QdIveiaimgCwJQr*CQOvg{WHk$dj#De6>RJRE&> zFbQ)P#NGczNga<)=6Hpw55mC6CG%leFx_A1V8{}j3}j~i@)d7;NCEykTlk6XHc+?$ zd?$f^ZBK2thzXD8FE3&EJ6%~(i7DX>45BCnR8X9ab@dN&C3dY-fvrg5XzQ+ukgMhv zq(xkM8tAR!4}xzt{I*~^Kv`A819mqU@Cc}-d&@ z3jk%o>sxPwQ~sxpsPy|Bz$n_8AYdtk4r$U{zt#c8Qi%42>xC~tI$^z3Ad-{XzT;dN zBH-7mo8@8WHpJSMTw_?Xvb*JVZN|qdCANz$)buDtEv&9B4_s~xZHV=$1iRp0 z-ImLc)ICg~pAAG3hz5)X0ubm>l@~tk-oK8iY<7n3rkw#NV2zTLd~ARL@;>JJDbEsf zMHWFp7YuQSbcQO82BU?>bM&wm5Z7444rs@YfAD3#rQD8`A2ALDi5#1F&1HnZ=3WCv zU6ec|U>02Ji}E@H?w^W(a{`uhz5dl_*?pI)>ka!dT=b#d|>t6x_tkFd_W`d z;OlcSmjA4-D8w0V8qM7 zAYH#8dbP1s1Oj=T4H^P?gIr);OprXVR)I~X6OH{aDC^NiwUGq8fAzL9G0+qV`epJ7 z=}e~jlce_Gww*Av62OAoLM4OPd=%2%iQN4Bhiy5~O1XjMuY0r9)AJMEcPr_q6O|hd`HRef452Bnv83!$igK>qIAZjP(4^8k2kf2`ynUVG{ z&8z_+VJ4}oM>q;VWkF4!Y^Llv0lC=yQW<VR9S&Q>tRu1}5zc2~ zkaEAwOEdVZ%Ek$!kl`w2_k;bOX?0eI5qXCyKFr}u7#<}Nk^&_FfhJjqL&elo{fz4z z*Ah*%8uy%hJnBe$5@J$5p@;AT3>5t%(f11$jmGT0RKJccUW4?7oD^*eQ&wGFoc3H% zvem4eX^>*N^3-S|i_hSX#PdLbbf)Cd0+{8-29@iA#;_)QK7yz2PweoCr$So(V)hG? ziC3)g*Zm0mIZKd~p(wKv`t{^oKDDG)cYaX$tQ|^ea zIZ48K8Ras#9T_NEot1e`6@mC%l~u|+vtkaog8L#vf8a4qB_k$_6ComcnJsN)Saf7; z_lHK6HSwy}%J^@~iOo>EDa&wAHR2W*Bmjq%VNrpW>zFC|sJX4vdT@JZ5uT40Y}>4_ z%mqt-O!$k|CT64f_lVNw%_Y&-2oR{!hRFa9$P(1U!Mdj;l0z-8NTy`Xi;jsnInGw#q&6 zhvav5;!stNWH>>$tRMin%Q*fLamJ30x&Ej&$HA4HKMOm>Q<-D?hLn{G%xan%it-I9 zXFLc6p7Gw3CT~w7;sUKbg;Ic7gUQ=+?fbpaIXo(jS_YNnqF5VHq!lwERp(iDF6Jogpa$VTY6q zB~ut<7KF|0DdJ2*fKJroT@1N4?mT**%ogE+ms#6@8}9KGA=wYI1g!^SaB?-+L;S`B zwwzyKNkaR318HHRUTGg5wP_8>U&DfQ?8mT4)!pW$Wug7UEtsK*%+wc;!xi%V4Tm;% znT6fWy44~Z@Q z&(S(V_u-+n;JFvLRL!KK_3!(`zBr#Ns2DaN=1>x!{gGHyoY&f4qu)4LhUl)4Gu663 zxRbt2!})E9r*y!v!rKs}JDS;yr}+qm1)Who7KPE^=T~{yxrlkc{1=klEU#0*xj6AeJNQpOBUIbpcHNGyRp%M6q#Vr zf3FZ^d63nS$>-01<4hZW3q;o5{Ow{S;R<+@-3Zrxu1}p zA3Zo%%2)Jx+?81Rc-y-?B9SD7M*#NR^0_}2{Ez{TCm>*Ja!jtU+ zptWh@-QLoPteMx*^g6G+O00`SK!641f`j`M!?J%R1);b_Mod?M}5HPELN`4r>*# zudbNI&#$y=HKlR-c})#u_&yE-*>ypEaq!>ib|1f%Uh~#kuON-Sy%7FbLlaHY{Nl^u z^HcZ9H;)ZL52hQsPAFciJp(SnW1aRpK~8*ZWJmn4rd8aPQMB1ZAdkBdO&a?FHU@%vLK($*c3JGFHrrm?hDe`Hy^PU1+L&&Nh}h97HoYXw z&mZuNohH+U^gb9VK7Pl7+n+0j0f{;b8KvbNS7=55M3&}CxIK*0%1N+a3UsFz@z*-7 zKydS0h(Gfc^KxAn)~szzTe&tI@si;k>B%1r>T4|J-z<>RN_e5E+1+SA{dxYwR3EfT zy^t``Ki&9nes=B=2V9~y=NH~s9x%z2DvA8CR`G2?s}jy8eq{#TO#g0`P$tHa#u?TP zrS}j*~++S%Gy7#KF3n9_?{UHM!N@2q0^X`Lfa1 zs;=f!NqVnU#TIYKiyOM{P{Hg(%f7-jx$DaJhTNpwIVQ}E#>+uhxa>K(wN5KYv|?Up zYRZsVxYN8n<1$A^_q5;xkGqjc8ate--PYM)HgD|blir^P|9k?!XHkmI$kH!911_(> zHS?iqEOi82zv!%dU=@1xM7Pt)1}=X-m@yoFTmMpW*`jD!^INlx7)!UHg;46+FK)-J z=#+^Gtp6B5wVRi6$nT<2SJ0Ct#_6MuHh3cV$51Z;;EITyQfJijS6>w)Vvi&TO#E3S zrFOC+?hst~8*6`==qi&#O5K%tS}8{j7%h%#ZfQyXAq^ZEuG>~Lc>A7r%3lIK&M54+ zooyd)1(T}YK|*NuP{F$0FZYXMFJ}6lS>D`Qhb@`73)4`PY()kUN;Z>ff(-T3k~;*g zSRk32Est}yi-hZn7xh+Y)j*J@#m0rUt?XpC<2(y4T^F*XzeKXIH@Sk!hmRk_Zd=~4 zoy_CdJb{COl^HUr2_#dnDet>F2mAIQAe0++Gq`!sq18l+l5lt!biD+m!Vwy(r1_c5 z_nz+^$1L(tQ(~keq%>`Y8lsuY5k7Y=|9%CYi>u*QO0@=bK82( zDA9@-BCd1g(0VDST5(%xLEX}lu%65{^v%mkn_2o&lh8s0t*AQ{P!hd+=$5?xy-K1+ zECz$xu4jz>(w+Sgub-BO!sEfTH+f!HK+)Yp16T`9?twS4$sYctM=M9%A5Y8>PiBmT zN$(<6qm?}j_x-omdF5PK3{u)M8hJ#hsKN4y(x{gR%G83e(kEQ4OMG5!w%GI2t_o34 zA91Bf7QflGVpj?1fdgR(K+ts@3s8bIs(*RPBi-X|ixW1YchG#1y2Rq~z|&e$vPWJ1 z^adp2l>O+ByPlRsvri3Ob&wiO2Rc4}YrA%UAq05iCIa1+NAdpR_>#s5!HkXy5$Z8X zFr!7A%?SdcD&imkKfi8^6*V_={@4v+{cK}mC=ajuWuKOSgB&7l%0!5CQFKb|UF1jH z|4gF#73jDf$q?`rw)@@{(16~(Sj-Q&qhe-$pE4PL7#I$q)zz5r{1T~-;~fc73Iy=P z&|u+`)bVLooO`q)nQD7aV7gQbA!RI76{6_H(c2U3b#U1Bd`9&l z{TTX!E+}GwZI=u`UTs3ggV*7pDeQsN2GC{#3a0gQJsNG0`B%j4P3-=yDD$bl}_&|c?+U7)~gAqEi0c5 zzg}aQcbpF``39a-p%9kK5VqU8M4axlWp2SM$Hqg~D$*l9h5iS*{#9UL%&_-228x2A zm8DtPUfNj0M1b3P^c3Rm8jk2+_ROpVuB>qO3KsvJQxkv_a`$~)zD>Szy^m=Xw_ZN@ zg_3onqP{;@a{e!ez>Iq(VmreDJ^0jn>Ak_3z`CUTmQ1kj&R*-7y#IsqFtU&OT3paP zevkKB2G5oM8iPg499;-x#ldDdDI=n*`Di2F_vekJ@>;|Xsl3OdXv5k~v3gvavZ|pP zXIP(qfYMIuLt-A2p-MU;&dJ6-blGE4Ts^OY>^8ub!7f1^6B{?g=0*{$w?|WT$0@-3 zaQ4LXL+>at+xuzY8z)W@V`~EV7e`$Zl>N)fh=Ii<4(Fs^%AJr$vc=4RZXJk5{Q{r=k?C27bZxf7BTrMl*J?C-p;O)!1f2hShyz(`yVwvszHt)n#US#6&HOiG*}DUV~g^KD{YBv zH_qGxQ;&$wXtxg^kc%SH@4s@d24O-^A)3X?8^v*ZyC*|zT3^Iq`mciNIPLKqe>K#o z=JtpLY+}3QUMP+qe|CFjNY5YNpQ>YNSG=HQy7G{CHX@XT)Z0mGX}Mv-KWnJXPx{n)yA8iWBBDkMge;LKmE>ed2&Gp^c z(@X{35g!|gV$QAX8<%m;?7Bogbo&|!KMT)39;j7n)EEU(YP|Nf+6_zCpM_Y>0au@ZV&Y-8qT*O2E zC=CJEE6(uS4!qNAjDq0L zIC_lkSQspYW#XORlR(y0WSh!cer@xXD|@Q%9W6+5X7^8bb?Qu#u%>OL?9aI)0`2{y zy#3WL=q!m2p$`Ok42B1<_NTtEv@13?-I-2ERUncOqtb5%_9C~-unyL zLx@8Xe#JibT*1XQRy)y4;|kexF{P%6Izt+J+$`5IIYs6jkAe?fn5XJ?ZqI^liDY{6 zlUVezi#aE$J0B#H_>~4l$X2btn{~|yFiw*F$7atGW1IDrU6`#aNO|x{#Qj(2O5=MN zVwT_>{+4l@^%cu+J-9EiYbTEO^=F@}JJA@uW4z~EU=P><63N1#wtL001)i}%C$CzJ zEc>*_XmhV;5Ge3M$~W9;K#U`Kao5*t9*#&mEadm6nfehkF%h8t8-cNUO$m5?m_45Ki2I{7Q0nEfhX>JPkqVaEmOjc z)8{d7aA)2b$-n2ivFBi6Y4jXHDgKdp-boZcUNN-37jGptQh2?};d$RY2?jNP zhjkawZ;u>=46Lpsar!IzjIEYl?b%4vyR5)9dD~2dYX~m3$K!JS116o<#X%~YP}^&c zUYkL3xi;Pn&TVhiVNKI^))Kt`p}a7Jyn}2wLLU3;nX8aKgl)P)QcCUH|3{@;ct!cW z;ofu#h#(C^H%f!l(1=J$cS$25tuTmm4&5-)BHc&~9n#(19Yf8)Fud#gJL|k_oj>8Z zp8ff(d*AztgptVvOhJ2f5&7;;-#_(XU8o<51#qzwxdof&I?Op8D-^=_kd5>P;BN)8 zpc5I<^y)R0#OT1udK-1Wc!%O^1lQVYX^Gh%3-67`jA00)-?M=m_BdZZt5shk{!NkV zLAj#kq`6SXmr{G4WSA;^gAD>-rW(&kM58=SKd{O`eJ}g(F>9>0+XT_dr@x(Q zM>S`N2a>(92nv0dEWvCR+;q7HM2E;YUiqg&#T)#oK9G+QfvHViC->bFW-!0`424Jq zhU65tZ4O&$?v9=Pyz8)MaeRe4nlmYPG{FxRX{m9;sI$`mTRcw4wKj0Q{+az4QAczH_~afIR9^WD z69d*4RSxEzNpmPZBhQz7t>0|AXWux?)d=Rv1QDe(-#jB2ga(_N;iiw~_58j^=It#K zltGBrk99CM&_@dn7%d9fVC@T9`qn;A4v zprIy>C<~365i1|o7qY!Bb5t7Mo&bJ)8-kjr!EI2w-ZTTjX4 z27+y>^9?Kf`yW^wrv16~CyvT;VA<&^&rsPs!E;U>W?;MveMyG05qIjzi@~V+1&@PS z-OoO%gq8X+FTx~#$)YO6Yi)}9?V}COV@fC1H)}6G?zO(UWK{8_M`X^e?NW0Sq&g8P z3{vO`I5V3)78xpZ4c?0$H)V#JaAYd08+_VxjuUDd+`I@p@VJ>3;mz&dE$RZt4gNZM zhRThE$8uoi$oP^qn)q)*!D7b^Fxe-IVV)x2?HfJ`SRp6yY_ly$Dy>rT=N5yroTp-- z|LueH8~U6dE=KYgu3!0LZ%n~sK@jbVIrH{_h}Cz3R`*$Q8KS-%@ww1=wwcV4EOb$? z&gbP4nk=8p=#|nEnq!U3YLP0syU1EuLQv@f?J)%efROq>TNJtfT>Fu1>pKj5zy)!6_^gLv~16=8V zHafmBek%S@)j)d_B?II}WG?ZM=ZRgQBA8!47JQ5f5O4R#6DB5pbt9|p;v)4dLwWJb z^cGm_qHG`x-&5l?_r$X)b?NC3A(Ky`xUhH~u;3yQo@h?%;dgB*F3F+LOPs7-p{4QS z!T{iHtSSEl87cG+`H*qgft~Iq)kg;=es55N8E_vLR+!B4LH7t8k{$lb5>+97#u6j9)O@AlGNzWZocsFTDjDEn&JUocSta+IQsJDtm2D6 z-&QsNEoTW2Sw=YtgCj(7>gQ zikX{K1%@ z_3|YwS<8;uLt_vtF>+v}(ML+f901Y5mD zNBf`te!1M9#MlLRVR4yt)`_dQJ>6~3!!Hu@KXB|i6nln?y}?gMMv{DprTZ82R(4Wl=7~bbSs|DN*YL`!$ zRm^>eg6(H~xa{;X{M-MJ4M-4s6BeQaTh#t|{R|q~mE8U0ODSG55|7b45%!M90GDE*l%!g_@7n73<11E#W=dSiRCC=U@6}`aE9*Owx zv%XE3lMLCKG9NA#|Je0a9@4yl2A`ffW^qq`4rWswe!(fePmok%r@VGKQtJEG-PV#x zsn$x>B%kZ6@ARB)8J1LAsUqQn``a6?U+iUMNku-|8boCnjv*NGdWlkQ)VoKuOO~3h z0hnI~FZ>n=>t})|&Y9u8w0jH@yN+8KO!gV~2Ob+gJ{y@5-ABU6P+2txlA*}O>r-}Q zZ6-HRBrXLa6+S)~1}k*?7>@)Eq*k+A`j1{4l+mY@f@6d$T1Si=cNJ>?rif65Z}`@(E4+#;@1(?udZG3Z z{UxD(tsSrN(NddIYNNb`Si&a;%OQ0%7i>8MKJUi;!eBDBkT9yhKvxc=OiCtPk9KtgI-g=ZdKe&_h zi{ty+3lEf&@o3LJvLzM0cNTn$uY>r@LJ-Bt>Qk1mSWcL>LCpjdy$z$J%ngJF5i{@} z{&N0MD2EEhO|0t@vl2ZEIw5zBg(u-`4hOzl9+eAsTQLdLvQodLanmdf( z^^OQT86iVn(KA$@mS^Z4s=)&GE|)GMDzmzbPFfNmEr#lME8z)%$6ON=s?0K2AtB+| zHdLy5>!3v!w8$CgoxsXP5P#E%mv3>A@3UVf)44F4(>RW*qdB)^5qR6$-gmQ!Wiv7Es-!6fFu%+~{+?QY#gFiB>7$=OWrnwRG*QFg88*MccLSeGmuMo0 z7qZ>|f!_m|16kV-o!**5mEzw%Y>xlKcmJ0W&h%l@tYnE#>ry0`~k`Y;viHIGzd@KyI_+nq!=`2j% z-WU5l?Mo-UW9x628r!^6*Ji_aS~0)AH0x&;Kk}s2%E;%JzD?bC|N|ZidjNP7?ACpl^?;Ne5a)Ogn}ORk1V|I;(M6Vvp~4Po1`e05BDA1FZTA+6 zB7}&I)wlVqT$s<+{l>|QrM!6J!E7XY@&Ugn+{TFz# zj@$Kp9cWm8D_phe7u)qqb0|@Seih0Ud;Xs!eSThc)!#JTO^L6COkul6eB54=3bPr4LlCT(0%(s6-l z-1F2iy&P3mH(lST4jHy}O2IxphkYWte}pJj(r~xaYpA-Gx;oA{bi~OCmPET3{*}l2G zZoLNE!}$W2Sx;g(nv1_y)1^(m-B7n;xNQXXRb27e5E%d=x#6~K!Ab+$GhiBh;et5<#`O3jav)4AfTXfMAO)|QfeE<9#JbQcFFORT0K*l#bP;6?+t>9{TSsYFwtkqZ;`fYHbffgcYCsE9Ry-4*e*0U`l z?3I=epgq(nPHM7!-QYJ;PY#=MfWi-cp;oc#hXGn(uyVh!;ID9_F9v9L1P!!M9Ih5b z>!BZ9B^wOse_{jfv8SF4x4AHg_|ypau+^Efua z_L9O?CNkt2R&halC1ieA(;JiHb+>~amvFt0bki@nk~xHsPZ8Yx3BYDTiF=D87T^Ip zQ@NUP@2#B8g4In*sG1 zX(_yPjvo&u*}R@@-n}^okUF!*kC3rkdKDv;lXI`3_d@-dX|y0e-4wEOmcOvtw8)F zq%f3puW_>O(KM_g_SC>YfJ5T%6G=aEsO40~9e<8SvnRH8Z0fJ=>tyHPU^z|`o1%gQ zsk_WjFGn3l_6srB)2pEBgi2jNE=;H@>}wPVu;(1#^H=S@HsBlX?DO9W)n|u+o4`WV zmgh_CHvqce)pvqfrr;s?Es588ry-M=W`J}em%_1D#z{30cBK_bJhCmK(k^M|c*DrQBoZf!w_cbrM&sg+~Bua6*NHwMWwKF&@S?5zf-N@NVkD;WB{aV3Wi9LI| zretJG*zR)-Og^$7_h}|Vpj_zuaQdgVc{o?b;7pvfl#<_|Vh;7nFA+0%t9E#_NC24A6FZBhblEE)Jv-JNB+z(L`cPnL>#T zhpdH>@fJZr2Pn1JAC3{E1MRHmmYYPU z`P;YsSc$v#+nYF?I?N=-w;2(i`oHE=s`KUdS7D~}Th1EitiPk;0zT?7sV&u)^hc1$ zy}w$j_Ve>$&XWlW0c5KWJk8v!h~9cUTpZEv@&VCGeNj=&*mc|IAqPViR=+-g&No+{ z)w{}hbbd{VZ|)4Du}*Bfem1-8N@^UunqUIDm@k?g4Q@QX&A*>ayRkRti^BBPK)y$n)_{cmK4$HUFeMu9$z zLmJOE%f1qw2{`prW(vr=%Q5vf*bloa1sUMB7}-+IX?bX1$^*P2cuyzh`T<1M@dh@2 zE<|zOUyYf*1)mS*w2%PO_+&+y;UX^&PqD-$C85(eK;1W0+&}@}PEhYN9Qc5|Q7Wa__%jTqID6#nS>20pKfB~?+4qbhCnlxuga+o; zd7^0sNGEWGjQej;`99sM$dDz7cuK&{q2P&5+VJx;XzwnmdWCDzQ6Hqk3n*M=Mm%EA zU)x6U+8L~JtoyIV+ru~I$%B0TAt>}BkIDV3wY4`Nac+yNA}CtPX;qtPSlh^A;LK$$ z5PhfJ_XpMvGz60;ID;TFU zrF&askbD~CvElCb)JUixE!u0p02#_2!9M%;_D6j^OS_6#oQUaDfKLpJZ1Cb^w)42u zJqb%Wn`#~fIPHF#>rI)NmU{yrsYEcfehl{kbw_OqD5<}&v%>nj>)#nk!EjNOQmbyz=u0-Yu5#iSAWFw8Kt zocr%T3=G&+XnsV-TUgX;EKMZ?)a=|^Pb6T;6wb|ga!=2s5m1KnoC;P>$Y*Big8qB8h}e@kzdzCV z5`veJ07=p#!tFih@g~0FEkO3IzsO90C>yW@@@rZ|P6K264uielasU6pt5+R>7{H*Y?*vxX(#HCw=ku+ zzei7(m%?)FRO9#kHKo;E^<{Gi=}S1i7&z|JdAVW4?jW^fnzsSSZRR_={uhZjwZ{l% z&G=YxucTp;f8aKnGroD=2IeqlLyXUfZV{GeIU1bCjNgkEoXJDglR=))6W^S$6BXrf z8t}qtP|7BiCwn;7RHWMn0MZ&2!b&*(oQH2+GQMtje zN@FqS?$%vvkjSeR8KoS`EzoxK)N$P+(^9lmjR*pgyUOc7jmd)we!`!ZBuOqy{)okP za@k%KpGtqeU}6rEO=Qy%{rQ8lzAjzx^N_NvEIPVexC;U25YCc`T_#3dgp0$`@Uy?0 z#x$#$>P(U2L+-)Wl;}a;_wkF>_0@ZjT0H3$%fb)Imo*K@0Lu?EC-tU%V4l0dX_zP1 zwD5RhvA{Xv)-9vaT};CTLGvyX>qqsvw*;S$eK!oV#ph6Rci0O4bk{*Cd_|^x4V!lS z>9C)n|2~nqOyMbrW&_7;MK4CUtcUZ6h5Im2$xB_dld zp0f&zo1d^msD=a69~^){KlqEVbwDtU9)Zo;PuI!14w1~*F|`1#Qag|A1$uf)`#`9} z9y+hNC)G;&y^ryO!;Xy7jRm`u!`+^#=@L+46;V325>-#>YO|hl_h#qtzBv+Lu*xC5 zO*h{dS>qUpdPc?ZuncEB>;rtLxL|q>M~g!e9j)+m{e-zig818*hJl1z zDrOL!JY05*yn56dma$H)4>Uj_$RaCmRc<^%aZvYlL{Q6f_yLCp1F!?Wg&aiQ~f5JWkr zT`mT}C1mKCnHSCc~a~9RFVPdZa|*)_X;KJHi?yI z0B#X$$vcf15T1qeLd!W)c)R!O5Iv?O@3q!zs&_mAam0M<{cm-ELVZZ#JQw%xCQf)g z2krK}i&53#m3{kc(uop!f|X!)kP~^M^RvxZicMkJSrFvoRFYZ+0-?4$ajl<|@f#)M z%kvRwIF{||X8(4*zx`i0D^1nrj;~Ytk((wjaJ)_P+vw0jsYq9hPN(yUst%O;>%F1v z^>W7gjl9J=9TA`X!bU>XC&N07Y+S(;_->c=>O-cOh=NU%*u`w73(bc(CZSQh-~?6(h~z%(QNZNBMmsdCRDzArqUaZ@O?U$wocWL*oTA^Hl7x* z+)tmK;ZpUf8h&XT8b7Q+8IOWhk^EG-jaJA@?GwPI(e<#Qcw7%tfMg(wA`2uy*%`3G zaAClW_I<&PZpv~=AR1uzz20SDf@pg%;kdOAx3ICoCjBlxRyh1~-)IC~o4z>I)myR_fG%{0nMI6Iom-feq5 z%Hw^N>D;JUSjqTWFqCD%@)1_u^U0t&FBme=C>RNGFdb>g0C3zaSc8U%<F{a~jeZ%!HFn*~dQJ5I#n7rZA0M~&)8 zNHjJ7lX0&HKL~W+C(gGYG5BTC2PaNqd|q+k0b!PqcmCB53EJ?W7=t3I_tf?W)*Ayq zR(;pY4gl+NB~z`gk3L*TetUf(hG6)TrQ22Kty1f+N-|q!K3nw!c75LUymD5v`@da) z5W?R=@8G%XHm&a>_Xhp%Rl(FtR^9xC_<4}aIY0g{qB|hC9Z$*Gbb{~Y{<_lHrPoEH z%J_JU3&ZeMVP+jOOp?T4VXz zp0xfr__&y_Vj9(J@l)RwI_+@bG&i$J_vV%cUMLyXg;P)8EPHr6Q8fBLf4R~1T=F}L zz8PGi-(XakznL;Om}hWIK5!WfG>D ze)XJcBbpgJDHFr|lsPRmNYkftX)Kg4rkmIU#Xrw51*^K6L4JOIgGHy(yyPkJJyL2_nDl-g6tXGZ%d{e3099bo7 z?<3`&hr+)s)$pc0FX9^~&eU1Z@&b4G%;qsMe)4;6F;pO*Sl^g;kUalj+#SjyRzFEo z?tJU)^bc#%x(?OgnB+X@L*H&{1R7w`36ES|e*sCRj>*YtX5px|dHA^oKbd?j1;1cR z;!umV4y!BB>#bY;3Xs{@+J!w4KN@(x`1>`eoh`;LchvHyw%6FRL`Cn;a z4rWPJ+!tQkZSk*Urv(UWqF-ZWOkT z2z<{lm=US(_MqW6MuUmQ_a@_7YJH0gu6d#MgZj=CG}T31>S>UI`aDMMXs;4bd-I0L zItjQ*T>)Bs&tei3{KmCb1PSBE;Xs-InB<=ieZ7=R|%lAVc zc*HI13K1GlfKz4m-K5zm0be1U(E#c zxfr&xBM}3?YT=7BAgpUZ>Jp9IH3?P>)tB=DQDPd~!_Bsd^(SA8%4Y>XZfU{vgLZ;{BM(bD$s-X+i%2XVYCq{%&dywSAh6_sCS@N-Fs1W@-xW? zlKHVm47CM@eiFhv%X@AB92cwbbESu&$bsi6@Y`$3Dw(dFD?S@x*NJ={k^8><4yPSN z44T(*Gx;?~v(mEgzZ}&4uJ7{Yc8)qP(Bms%oCSxsF293?OFonFx8sHM`)mpb>Pcg0 z*-qjW8=>2&} z&Ia$xhvO3w_Z2gRHDW9pWyL9EeO2Ge<+cfRDSQQ#+Pv#|{rqQ*(s4vo zE~%M!Ca4Zb#>QWtjS%?vHBEj!SaK_uJ7g`A-Yg~((3bdp7M&R+d!ehO@by=T=d$kyU`CLvu~b)ds?ethaf(LK!9Dh zWw%>FPln=X=KntRN`9dj180@hBG{)|i@CLN|F%{t7VMVUU`_s~k|y#r~T(Wr^xme)Y#40PYOn|nuNen-t@Z=ZZ*0f*p^fZM58)HO;Rpz~@#6z+)% zbssoAogF0q1YruCi}$-XZMl|s zs#;Y^9hkY?UG+Qr7Ko5|Sw=|GH&)0}``Be$vF=3xy_O-N^qhg-eD}{eB!aPrQQw@Tqk+a;jzj(Ps-LJ?kPucyi#Y95($o_diOE`!rovamhxh;t6n^ zwH^{IrPhTg?*ltU*yJLK*>yg5u6YSh>$@CjWx1##Ezb3L*YiE z<9W{Lf1WaXQX*#dPgNRf|O3ZCc)nTSpN>ay^21dh*ze)r` zp2q2f*97`tJKNdz0aCezKbh)8B5)p@-S8}O*0G+QRYaUMYtl$Ha4&@2m;_FMflW_p-T<~Smz5FZcB?gRVhLMRWKwjNR>{K~tv+Sfi8jWi3j z!#xn>yP@)3t{5lHIdT_?P~*6Q@k$<>`GP?4!xdWpuXbGk$#I(@_=X5m&&~FXvas@8 zG@=2z@o_a2l1{H!R}^)nS9&k-%US(nUj0bI*_Tf|eVFbY)hk@cPt@H-n{SIRo^?JZMmz;I^z0%jb1w!@_3@W z**!32S@}!6@_Od9h>F$PHQ{AQJXM1!>VC)fF2NAAeM9?E;|*4*Y&$FCHix-u_Tb0m zX_(GC-hF$$)hI|;#De>68@J^sd-4M7m@(F5gu{HmJVG^xiG$#UWWk)b&;XJuCPyAX z!0^0V?eF(ALHhaGrAZ0#GqMjXBTT+f5`gu5GN^6YC%_uW(>ZdtFb(sZE)yW=X3Bq` znc?&hgRKMGh{?mk?aSN+foV|wH)(wr@u;x! z_^rMr6F-z7nw$NHIKlqOBH%J@2e-)k@maNl zl(e%*wIh+u(@B>*myq5>Uq1KwwU25bwQoHBhxfHQAbUlm;=LFLgbpy`Ad;wA_)fyKz%R>X< zii%l`w2^5bP4yjYoUqLu;_KSyMQ;})SlGVk>p9F$j~p1A=k~S!YnHV(8a;wv z?$FVa>pGhw>m~P7u#9oH#x?G}z)Z8%;x2G2$dN9IO|t+inXU6jmnG732MBkSj&XjL znV+xqb8=?Y*gn%`%1*!vmn4+#tXJuS?IcMS=P2Ugp@W$jw+9QGgx2{6reQi$yAr$g z+^+qa?U>ZP_$J1nc*!|$9&uKzsB2EzD z^D3LdTe)Lm-)dS6P#Cmuy!CKP{})~7;3*7hq1#)O59n1pu>Gn;;71m!FP&!jH1BDM zu#psY(<11WW6EL_G6c!Y4G_*n6sS+w8a^ISyJT#TdONj~@uf!&L~%EV)CSG8veo!p zQzQB71Og=V8pXlguxk$FTkwpvZoxL?E4t|Jo#(FIci$)Ld%tH0{D~Sdt6@zKJMlDv z=aOEL^_oEF*s(%o<=GjxIhZa$tJQ1MFr98;Cdk~L`ZIts4bsA~lSOT44C+aDjvO<> z!qstjp*(_9ToW)~x)naIhmia{obZZ6^p^O3>ASl9R{scE&iXRRg=k*z!Q1uC)p!)e z6NA*ZDANm2MAF6Q{w*RIQakOyhfx5T{e|)hyRBA*7KNg4X!UN8a6C>6ua&%H4zEj(pYi-&#$Hz-} z1u>aCGbpF|OWHQasoXHNy|C0JiS~ABWj`0^hq@F%oIcS9tF3!JKAG#b0DuxO<0rnt zv6W@>G5l%)^H5-x5;ySsudhq1B~Z2bJlVGOk*n)ayTet9uM0xg-IWJ7XR}|Z-g(pR z$3J6PG?aZ73jaM%F~vB5+QD5=${w)QD%$@X@TnP#LY>IuF$3Vkd3Bwe36IKvT+z`s zLsWV-2n9U7Y;NEc(PP(OF#UvidxXT<{?2@&+i4c+t|R?QBY1QuQN*lehl*o9-+wW1 zs47_Rz^gxE=d=k*8V^XmDv%hsa9JcIRQ0A`@v@a-V#Jb4`wa=MGEGUlMJKAyk=Nr4&RI7^Nnv3S`h7Ge*lNq8gm|atb zO6OFk%`!5u#sARx^GFg77}BmhPhfMo#IeWk`_qc9)s!f^0o%szv@Z$>mCt;&ROqcI zlmq#B>a3oiSDryB9ggTJRd|i$|C6XbNNd&+yCeMYo6|06L0CS1J*qu{%jQF)Yp?$x zQB_Fynv*3`v+eRWln?IgO{vQVF_2tm1U2aRp^Hpn-EU^ zbnF^TSyHt7(T6xpEh^cW_wlycd{BL}KQcl1oAhYS)-A>JJN@n%p@A92d!_$HJ;w|( z^{_QG2E~huyf;@PNVqt`OTYEm*Ue&dg6G>dHUH|e{9G+eVH<7Ro5q%_e(n6QSG`#K z%zaM(i1RO*V+yokpCZRPS->va~aG{Kh>Y6O|Syho%LS!awc9#R4gckFMvlPhNJ@AwC^x zJ3*<}`JUwjEzgH+bt$wVm1>fO`g0iK9(TkNHzrZGLdH8g@q|-lO8&^YLz7qY_jS^Zbu~Ecx9#nanHHzO(_KPr_F+jA3FUU-J!U}4+-_sPQW-~nT-NVNj zusf8dp;jmGb(uIcG~9_A*FI>0>ViB#a{jLWqT+wc`8_&$Sv)_gU~vlI5_%4{tenEu z45h3z8bCS3p8`!{FsS4~K#V`o=L05OEXgKQG85yUVc=pM)xt8pL}j(EW+*EJU?G_ zKNAVvGo@!5!+eFjT4{4=d=$a2!DDs%srHpmqjYn4e6%V2V;A@IlvB0O+(?N=>}3%V zQ?aa(|!(9Ke)y6ifuoyG`A8av8h=Lj1SUN zZh>lH z9?q3<4~+iWv!_MAji27P+FhuVQ{{y$vG?4X{Y3!?P;P<`pevb@7kibn{*R97j-%uL zyoMex9>E)3$f&WJWAF?dv@SDSrT=o{t}N_`@NVm&vJ~EK`!2_Bm|Qyed$s@*Q;EnXA;s1MNUSl_TC09z5^y8;vQk*DugeI>4FfevbUzVPkL1 zE)}#AwD%?B{RN!a6^55(?%}UI8f4BeCNr0k(BT-C4-jAUT{L%kSs^s0^KVPxl)&)J zXt>8%d>AEgT(X+tASsCR1<3`nzQ(0Kold#gEua=QDC%Io;jZ?3287;p`j_sgh_lFt z?^*sa;I>4_Vf_nr32`-e9byo7L)BsK_96V`RF3|W2M%BB+nM$4x0$ZgL0*S_oh|p5 zVUDL2ZpF&Qf`4E9!z(b%zWp5gG)VL(u6wOKTLx7$^6FIF{WYcUU4Q%DAiu8X_d>Wf zK1sp5!RF#NRitUh6<|0}icC|3M9hrmLL@g%ZtYLLRb@d*$Qx7tAH(XPo*5x8b2JQ$ zp9$@k?yFM5R3>+(Jr7?%i~OA(NA)R6$__Rqs$(YYJL-Mlofo!P{|ya{|Ea+d%@j)u zfM#Je%G4wc(N%N9$FW44WYQPbPD>10w1pa+2nT z@Q2*-9Cz5bE}ADAKS7W*nzE5t9nD+8D}^h87ifjxYK+6ORV3U?rCFMwycF$_r0=h< zp6C9z#f8hyl=Fx4VgzWOa@V&kEarn~hT{i7wg=7vPCpGE=Vg6bzX4TBwJU|*+Ko2_4gZV_|2bY1Ij#jjk2T&d;*Cu2QsS%eu1Q ziS;-y4NT}t>_5+Y>+J<^{$GR}Q;KT^CfT=B11Ov;GCQt^`#T+Sgyo<8c)jghUMk^p zO}GV}BRh!sfb!L==;64a9>fUA|lbCgVcqI{y@5JUWuWq@XTwj**MSA-)CL39UPEO_Wl39|n=fyq_| z`-$L(b!fFNf>ky~$t#WoMJ`G%PR=ZeKKT@)K_rVI)SMokB7GubLb;KapVQELqW!W3wB;8A~@}7{}pl&NmEw`J-)-dS(dpwy3 zFQz*>XgCn$2wr~Xp(-v<>Sp6ZiYNXJr+x&}-Xzu`Lp`2d2V7s|wi8v-ruMj=!IIH#aYu8XCk`+deJGF{k+n?r8R;p)J9S z9n|5K^X_k#m%s(o6N&BdL{IXJg8G|`3z6KA?zQs#?uZ5?RlE&*54ZZk=09ra#`M-+ zIa<*h2?8+^qQ0gMgRQn(sriYWHYsTgjGu{hL(P8Q?8HBnZZ9P6yoq84nR-G}>haK- zHm$I>4Uv-%a%u#Xf?kgWsiFErt({5~F*Z=lu~d`nS}I&jr8M**`+3Jg3EWDarf5JG z{h@R3@9(WMnH);LE`Rk$ZGf7h0rB>uXKGiy{xpQS8P6Ymfvra(+VhMQf=e#m|Fk2g zMB<%^KUMh%rQSwtA84Xo&kD=~+t%isnLyEL!q|YviogkBga7zn;qB9$c@kyAk96$+ z&NtNAg{RAzN^j9*C1=v%12w2#-(1X;G=yFW!w$xO6rQv(*$%S~-J{4^)wknfB)_Xj zRDbfe|LKOPzPKQHn@cR$o$s(<|61QgD#UbYra<+~dMNANYuEL__|%}B?jTKboB2bW z!v{yUGo8-D`$eFLeVvUyiSv!sF#bm==8yI&eC*& zb|rJkwbOQ~iAapc1{PMn-x+nwx1HL3=w6UVDAPU%28b#D+iJ@#R=H>{=1$C0^CZVqUVV|Sg&p_bL#lA4|Nb@S8CXA-<)Ow1=FZw z?Cryum7-ZW_3Xp<_vf3mx#)o3aT}LWKg+*AG6iORF4^(xu&owGk0}vWSUbiRKpX^8 zNrB3Jq`y9~IZbqFn@jo&SuG=<3?C2tk&Shp6;HW{+)|H?$v)Ul`(sfoFIMP}2+~NK zt6rz@8z%m@58dMQ#_lfvdImB8(bzUtI0KWDf#b`tUPDHSpcQTqr$*K9mP{chsgvAN zEg`_gYcwbWw9wke9CN4y%j~mtgpF|w(~iHxSb-96h~6miP9T-XNQIx*waKpvMX&#l zi?0rA@@?PW#()vR21rPZ4hbox*`zxpq`N^%6ePw_kPbl*q(LcZkP-<=X+#O>MtUGH zWWetL-_PfLzrX)Hj^}=!`@XL8Jg?dcR{Bz?2e)W?9x~1;{_*)*^<>!oyL+ojfXeFhaWARv z*cu#}t((5To;R%X$^X7yt3hja7cRNS3pemcI*18P`YSQtAvuut`^Q^qH2p4{h4149 z1m%oTCNHIiM_WA>a+5Dy6$4tAb@k70>i2>a}nv2nce;^e^gtUB&x?GJnpqISZt(Ps&F zP-)+(3I%5H%3^E@$G??}9iEQ-^!;s(oq6-mU4ZZWZl@pd9=un3UT#^@)6rA$O1fB# zleDUYs^%9zX zotn2So7q7Z&{2_9WP^%X3SZBoXPg}ouNVLn@GjnOr8vZJr_g41fWf~X8x#$F; zqFVIJ1GcpnmA8Hnai9jutf<6&zUCi=U(jeov^C!F62Zt%d~%L@_>Q&r+kx#s01#Vx zgT#oYKmO3&EF}f*|4NM#B*)wW-Ip5nef*L$B^z7qDy+@I3|`7WGDO46vqLU4EZVxm zS5Sb92O*bKAl>($>!=YweE|T6fP5Xy!$=Zt^u~j34G*8XA(wEh^$d9uqu`y}{QTW3 zgyYLXR#xojbU+{SscA*Va zUiyYgcT8dARFhw0!t52Rbr)XHc$m4xRez${^X;GmVd*S~gmlt?P^fiHy_@~ofm3(W z3ZYPtmsXn^&S*GXh;RIPL=g7VzH>UM(4B9VSlS9U3v!q_SHadcu7JAW^3H?Wh_qF% z(#_8i@by_vK7Pcft^>9dczrGI9j_3RWBpU*I6h}H$<_wRin3e-)VlGD=@A8-_?|2e z8_9L*EA1qQ8xBtIa`PdqwF|A4Rf6$Is$sIoYHmKn@axqc_P8jjXFWa~r=_;Pxa27@ z%gyO=?^nYkiQf|ePuLR5SHKyw-)=}$7kK?zS=wx_6Zkf~_q_N+1l+V(6>(L2ONW3d z4m%bC3bN`7lMRdW)4Zix>azhOod=C2nzamV#x$jO@>1cNzl(yk`6-!na7At&tSB?@ z!7v(x%^%NV=l1c3IHG2_V(&=Sp6%`drOD{jg=KSeOQx}uN&K^ZZJVVTJP0wd;_kL{ z751#VH;w;B`Mm$jow00<}&H4^DHUK)~|P7m7<5*U!fHS6`h4(sbV!5jP8 zbzr+?ZxxIn!HoHZ+X0jKoZQp!1jPe^XOBXX;W_G zYR|=4HUkSd6ba&3V8@IB+I#^IKn(=wyPiy-jT<@Q=yxS(9=xS=Py;ZB6n(1puF{Gd zo^=Uq3ty`O5P*zG(d2(Di+9vMglY=hC0LH##Oux9#8fi#Y^{ib+ImNt01tpz-J8re zz#~L$u)9pI+ddERA3iMpDKWl;z8m1ADE!AM?Q-C-nty9F^!AW6t2s+nLU*>Q$j9*_lL^hF8k zfkH}l?;6cmD{1}@F1b<4TrAH6ZoW0g%!usN=k@F2>ixrHk*BbZHzK4|WR?$qscC&l zv7F9%P`v#US8~rXbdHfX&v=&A53_mWL7_&;IVx?>n9uAnv1#z5Xd#gko9PzURNPTG z_d{vkW(uLtVj~47k~`}tOZ{=wh3$mBM#f|4b{{UhlvN)ACU!oeOJv@l`wHUy?9U3gh3uRM? z4O8Rg#F-wybBg2RJ&YciR^dABNZh2lza7-f*1dT52I?sk;`5$!d1Wv2 zl^EBJiMF2X3S_=X*hr(jb4*$zIlZshORd6=Bu8LxGXIUbJI>KLIZ%x>Us`psjINGL zi~POyWWmb*Pel{X5^)(#FZ6kg_HIFlZgiQ_>DhEg{B#NJJ=Bmbz>VWT^ZaSsKdvApI{OBwzE&};6Wrj?ukxp4`g@< za11m8S%p(52evsRhO`>Aeg>cbZQSnpSRT8lO!)zH!|ER%SliR(B}c^C*rjAHg@eMk z^21ge(nzL-k3_mi=)0i9Ujv7z5ZkV`&@@X|DukJy?UDLh`Gx`;7=y2#mrU-TZwE^K zCYBtsc@zlU!8@E2vR&~W`guL#PX9^touQv*dqS^+dmpbqp{*3dz0A(TLp|KK(KqT< zwf)a2WCm~f6)uO8bHka~3imqZZf3JSdb@&svg6F3;&?{UGE5X*)#!P|;!K5K?R|BZZ*BeOpdds=&*zIbD=L_vA2% ziTIV(%_@)$$XQ*{I|5o(VH{;gD)e_<#;4`_$(IdcDjHH5`Cj-Jt>Ynj-tRBAKIl7- zwH^6V1#B}B!RYB4`*OdGi50N-d6GXBvJ^qS>sCuV`ecBqUM%V!>Tw-@0$tk)fD<$c zMz0s7+UBdz-~m=&M@)eh)*`STgC)PJNq<0EWTomz}Higu=b*lCOhZ>=t;@yi_3JY z*N-Xz(S@R&vxIj9y}M<%Eq+oLKj7$^vFBRI6o>u1d;8_~-e6~5Bs||e7L+P|0wvJ? z8MYPBWZH?_keAmjxdv`2CNYMEUM!)bxpESS0*2lj9wkYV4FQ*?7;H zd9%wu*LLZQArK%r+XA?s9A1~6X3RSfN!Jzk5u0lyiH~NDq^A4BCMZKzCQXH)Qi;K{ z$yb>wX4-VN7BWf2qP6=WW!!E)*7?|+fV9yAd0&a9mC-MDvrWJSO0n6)Nm;{k)A6P7 zJf~ucTiePcu-S<(2cq%$pb{HnEnLZ5D#x0TZKBVrEIXxbgA5N1hUu^I3Q1>U$L zubA|_u#I=O!c*W)e{{i$TqwWY-nI4bymQ|Uk`R1rJr6tM_1ZtQ^YbH2ZO^)DW4K<{ z8VUKmHP z-3?C^jRLM=NtS0FdObK7zmF3{g;mM!eGq4g!YBAMwhK?g=F&kNgDome^!&eCf8`xYhd(oDuA^ z6t2oZyVVH>#W?4Mg%7Qb_Xy2Ju$0*P*AU?-xIsi%11$3jXYkHVe>>v&}Keg`r1Ka(5p?1N~uBH~J<9O@&^PusueMhIOR%P7O1XZ+b zi{LiZAZ4<$N!Vf?PrBLJJs&o&C<&&0YlO=1&3^qV3=Q2E1YvY=f;(hoDn92+!HT%f zWmn;5tPr14XT0mZyUkdy!Dp*{a!81{7V>{*^M0JDjRVCucP~Vmk|ME@JriYNSf1BX zd~1&KGGirI-P(;hDB^}#1#H)FZwMHS)q}9Tn*O?PfBpIV87Il91 z`7JBfJ70BG5FiT@R4iwaH}kl=Yb>tfj9_z6TD-IJaV%%|B!i($0@tr8mpNHZyM< z2HQKAZR-s0`R)?<@Ue#L6{{~7*XIhCin1_+L(x#cH++O*F-`Z|oIf%mXXAEtcbw(g z?7>4GF7AJ_hB8G&9nncB2ZedqQR_b!eGLcMi&CZ*7Tti-8{w&~IPL@t%>T1* zFaL`snIS5cuXr|rnPoqHU~G;I3MxfR9*$}uQ~9!;Ql}y|xb2Px)R}}s{Dsb^VA|lJ zbT)n^4Fr1I_<{AeoXgy=%BMnBI19eng>d%B&m%SF&ZO8pRylcjp}?QO(u+e1A4$eO znqc~xmUDpf1Fri`!ZrKmttShgc8JSo{G#A5e{^$(X+X}QgZ+VTM%VgfuB)04S!nf#O zDxt^lsiI1Q(o@Qr%uLnV-2JP$AvAbhcj4%!U2uon4)Un^av_N#5Vn*-c$e;BgXHGp z7*#X{uakEU6Wvpa^b7e4%V3|-~zJM5! zH^06r^v)H3z#K*Jxo5k3o_;!4XU?bS^p}f|vpI-=^QO|PpCqG&wh}w*`GtG=x)VnY zRfRR22^n7J^NlC8!&$BO0)w`Ms*TQ*=Uq~=clCUekH68Em=pSObS&1ECUI#(t}7mu zoNqPqD%5>%(~w1E_NZ;Wjtx96bSPRKl5v**%sEr5Fi4wAD+7XmaG=TK`N!Q57x?&i zu|am{j^=YGgf_!(Sr>f=)y7GWhvl>KIyw>eDlIDR=#^fSwBB%Z5ctj`mA&aBX?Wa? z5naUy>y}Jrhs1DS$vMIiJaB$~th2;o=+8Sf1>d`%kKQAYuH!sSn+-)vW^*s;96K<3RO z$+JAgS-Fjo(xR>-lfp5{T4x>Fo%CC%VBAcQ=w>yn{@7QkfVjrX&76MaYZk$$_!+rN zui(X05Bf68iA5;Tc%%wHheAPF_uFPxHOcwj{-{?X95zZTWo?0pw-$HF z747}1Ely%H9sm6n-ct~*Sb6%u`jN!%xdoB|t4!ZO)2!*i2~&8f{WDRMpe^dA2>-Bi zK&IBRz~xP{T|Y)#^_p~Gy?NIki5nlcM41J_Jn92q#wce61E!qKS};_Pc~Cx*q-nL} z+F7mb+q!pko98G7=+8n@_H%+R5m!wBDK0N#d~bYqUFDTD(VxJ2!uQd3O4)Jtweud+ z0eCOC_IkYL$9wg?$l}T##Y#(z*_`C2Zy->2;>ujhj*KrJ2(^_O=R@0;ogjp z>%g|2ZGChhOCcrb7O-`2uw?7VqTq@p!mP&M*!Rf~y7zZJYK~_sM~W<(l`P2(-UoGA zWGNOf%jbxe$>z(>JYR`?VapIIzzG*R?QC*pdp=oXdXkl|aBeT&RL4QQq80BQWc0!sO036^%9q|%eHKwlm-qNU*7RF z7r9%zG2h70ch}w@ zeYfkfb2iP6!pg_*`JQe922~S&m8VH4y@f(Sa?H6DR{pLI{x)NHTaHI%qvE;M2~R!| zhCB`yJ^As==jVy&eCOIwdnaLAZhB+YZ;uzjvgv-F;&qnPs#>%)I*Wp84vou`Io`EU zPguwk(+ZnT@^C0ERaAztp7l`C+uk?4WotI$m0L>9em6twMGBBr&*?<_hcia+3c0LV zYv4}7-N!*RvODUF>3if$>o?aE_?%e-f3#2$1nR~KiO@twMXnHwPu>nDCXuY zHpX&mQ*g|6)3K)Un4gG|{qwSkiaUnE$-mB~<#0ZkO}z2-OcODb(D}v5Ibu3(2qXk6 z*ETbe{g`Z=Wp7*dZh6m}(|&20B=r26;j`a=bjD76>gvbP0-k}%jcabI+bz^7JF(Qv z4jwmY#HTCpo0@d7wEVKt)&6t5*mqcGp_zz9&U-f~y=5dY{&^p!GtY)KgRzpC59e5) zyh(3qGi@q=XtL*Bc;<|!gB4^_pJ9HnC3_5*5Yt9voDKiq^ z*c6315hK=^uioO`y!FnJF~FuQY4TmJSL~3{owMnXlZ2kjCZ6u_bKbRvkf-)>>M`-1 z;*IP|U5vI7OQvqqTWIE)hNI5yVROW1r_Tn|^Ye?H@to*_Z5+L%IZwy5mWL)l^|*e+ zJ^i5Z>MTgKm#mNPGQCyVucJbT;}1NtZCRC#Otmi`2iIav3tt?|48E>vtSOwB(Zjm< z4@#JziA_pVSR+z$E)(s(wM?u^F)qwYUJi*6$bRFK>Fy*@@_a&)CB`nEGv>)2I7aE~ zf@(KVmGY|E^1BT*38Y$NZObkP9s+_#F{YjK%ZO!)2d~3d%#p_s24t zz}F(Cx8A9wCYNZ6SYA#$}=>|Q#Ew- z1j+0^_spF_c4W(gT^pt^>2YJ;bABEc1)s|^!s^qM%s(nU@0eF5)xkAB>(>ZFLo4Ia zPh!Yj|7?8r`SCudIEdkW{T76lINPSa>X>{uW2a1CC7m(O4(ct0=e#uya6| zlhDQ_xV(Uyne(ylW;4Ljy^PL|2T0 zI>+9tWYJ#LlXj|UlH>cE7r#HdHQ?q=8;+#;ad?!=$I-vFveM4Tvt^Db*|jM`8Jx3n zt-1+sr>Cb%+MFjhH;_hcy-PAv#+w5#UgS^s?RXqlKcAmRF879#?`h~p!Jp=kn0XeV z>V*h;{iJMf4Niacj(~Vyo_!k7fj~ka5S`z4Q`0cagn1L`s zf_q%w2-)-&*arJ-8D~m8RSfIFKIVQuAC%R!(M`OWoF?H(e8%=r*%+#d-#G8WB{&MmYR7(ZOe^NcL}2Y+=y^WaiAUL~0h zt4skMNv{7-n($I(2qYv_6^*YU@3X=GB#>h5BH&wzF(J38sNLHtXbD%D<~ZwBbyLaN z1}dFCoLdaP?R71=l!CsXYuno!2~*rGuRA-FQd;T> zY#$(AzZum%^6P_Z>jYcolR%vrP+|=UkR@S(Xyd3b=LO5`dy9Ih?<6gW1I+D~H;D)U zNvwdTZpkB=vWcy4#yx}_w6xVaSD(KL?8a>P`0t#VR!r;={BF_Z5UI5HB8!HbYOw}g z6>e|9Mzk?qpAU+HaZ@yHUm%8u{Ade6s$rssHP%DB+3cf+$=|AXo1<+H4Ff2F7B1Bv z$DsFpVk}=PM`OKM&FD-Z?93o4#aLD&4@!LcHOk6n;%)nfBjzO zzIDWpt|)AzK}O1p$ba4x1mQsM&OTr)c>wmW?{bIaCae-)?MFchTz=8y)oyd{6}Y8{ z6CV=%22;R^!^%nd!4%@aC>Hq)O>CWdWS$~x90k^ZwVwtg$1F=JSpSE1f@K`uCXeboqrP20t(>5c{D41b7l?;H5 z7pjAGO-bJK2O&)O5JWgo!YWCKI4crp*W6fW@K_ul-TH>}iWX4Nt~N94hj_1Xn{%(r zbM;CQDom19y#=)Zc;+R#dC@z)EFtpRcW#`}v>(c)hhnxk8eycR1TjgeP`n)yPyJ;8eNf34);B zo${3oDB$a@YYPN8t=x|qb15b&XMnb1zgjX(2bFDt&6L6du#s7>etXISTkhKc zq{mp5Qy2$MYZ62n*nm+soC<>7_`0}Z!egI*?gIEwG0`T9dM?xC}3$g-5assfI-)vr?BeIP` zdE}=wk+QSkp0|kt-hY1+#y*swuK1VvOT0kNlhf=5KPksE1)O;JhnQP3z+zHH z8Ma^6Qa}rBQet~KFfrUAG^yUc?l39q??wxb;p5-22(VqjUu&7c($VM$`0!8dc#GHwc>J7& zaRw9$f-I;g@ia9}0jjkPe;P-xJ#;x81c@?f*dR*R#1m z6eI{k5STFyxhZil{#D2I z2!@M!sV9=2Z>cBJ)7~ll>U(s_K2-l9?7~xUss?BMZ8qkqIfi+rCi@x#-#~7<_P6K= zcq8Xb2s#4pJBgq655q%JVF}xILd%@WT|giV;RP_);sJ-EO(|1L<}5CD#%o@Gy>{|U zI64A;GDVW6DhgJ>^>SP%ItW`E81fmQgZ-YM0P%MI8@pJUi_;!`VbAM7s@?*91ifJ9#TYs%3BVTd}fL<@H$ zvNN9E?YYK%2n1Nq)3iwMg~LGe@>9xWqQ9p^qlI|WW_TIrNn6loq-{3yX(AP7O=2l>5watSkWg>vK;SaJj!;CUNtpM5p4lU|rxq)hCbOcYg8z zW#UzF$_a!awB{bd5LyI$9n%WP>%Wwf^lzY8SOnj~Ga|H-0bI1!8gVi&aixUxc?z*I zFL5bNt}y@I>F>n2SL9Wan0{w`60m>&9xMUjlP;*r^*;-Ut``{n-vx!@oI1DK?)2RZ znfP}o>gSO`yzp8L%BI1<)|Lvtu&sJDo}NVA4_3f+3hU4bC|&&nlFqPV16uo4aR_SI zT{`Ul^*0q=g@9SFhjv=8D4-ZNn!kWN1&slIKX?*sbgBHG#*z|>NgAm@`_-Xf2#i~Y zU6ULV(#aFz)3=mFja24A(i8@;aM6I|+GHRRS2{>Ao>W10#>eKoOTTsvOt63@h#0-= zfvdPaHByeFO>QLSG)+JVL3BhBVYx+(p=>=?ci;twq645-&`o!M&x@pg)XDBH#00aiZY5!lGMguq>55Cz{a;y!P({~|C;wDAMHN;j)2#bK2RHhlT< zDpq~+RCWCC5_zHstk*$z6xK`Fif)ecN~BkuPi=#&X7m4OnDtAtbGg=+-xY0>#8m%5 z6N<5=Z@T^)D{SP@RkAUsjk!xG8{wA>lsAAdgm+{E9uS7utt1g9 zREFSgy74@ zf&r2sqtt}^y+6ayP!%08yAsF!N4_}A(uizqYj`#2N^vjHMuM}pa69TNNK()d@QH(z z8i^|oya23vUOS8q_@^cMhqAzx04XSdFa(ivy^K{ED@xjm(w<0;IgW^ypGgs57GWb` z58MW{q5l2Q&DHsdX%kW36xjeh|0c`ZuJ?&!ly&Og49TF#03nC- z)jSexWa}4SoL+tlMNeO)^gGH_r0 za9+i*@9eud!1(*R2hb19{5{N0S{3Kll`!iMpemX12zVO-4{%boWP!Fq1V@2;^BSXk zh8{g9aeQO9g}w$9igH1rm}PL0@H?mq65N8{cGk-Ucss?6!YZnUd^eI&@BnxM+WY_< zfB}L8SQ|Y+9_R+HBtxR-hk!n-HKF$Y+p}-8*F4vLhYmY)Jx80bAWh!}SSKxi@rD$^%R0 zCn4o@CcWF4Vkb5f=sK4&ksrev?V4L8WggSO-#W$4@RCCfjq@ME`Kp;S62rcFMcR<_an>8?q6_o)n zcv+i{21iZD4)Bw4zcXvU4G?$F$1ZlJ-6)enF9KQ~9(g?nW*DS|bpg~slZ6io4`dY| zez0@-0c-qh5Asx%8EFL|#Tuyqs0TKYOSWWyp=6uzyF42%%D%A#fD7=5+z*2ykX2a8 zLm`#HFq`6QpYk&<7QPKhH znxpc-o>rc$OHuS1pbtn(fOh>vjLthG?+PtkZEU!c42*fOl=wI^kM9Xp@(f%!XSFc) zEux3bqX~Qv{WdS0TKagJG$;KJSgv0>BJ&HA1SKTyX}gQ84>O4*Gtxqt4h z%8>16u3C#&oprlhNn%c7VUxy<>WBSqZsWZC#t5e8>+K1 zdb?v>jfkonlOS`F=+2wL8%D|h*bUAdZ6L&psnOV>>4KRCb-iYdT#BHUy+>>q43JR7 z8CkL()LiEP1|SR}(7>&LYXDSdeBlHo@^^D;xQ|5&Nd>ADd_>0@0!_Qf&&F=u?U_T@~FZ_AO%qH@UfJv6qPEHqBxFpN$ zml(vDOdM#X=}(LyMN_-b;(dknMz@b5R9flZ_rz(U!sB0W%C{45w0s_Cw zK2V{f=QpXXj$G||bP;IJdKIvEfX}GC+q)yMUO+pEFir3Z`P{W_Ug8D@W`tad>V&q& z*pUHj{KhSQ5-LPsgG2uZK9=zmcsFQt(jHk7)-5R$yq_aCu~pCja6 zxVr~(7e291_x_sq>DKI|xIh^M)`i<|&nkC|Q6$n4aHv%XDrMT_;_cUT0E{Jb=LMMB zrNB+n1wd8<+8lsjAlA&-l~pSK)SJh%#Zc(3Pes0tjRq`0Q~m;rwkt-yz57TgfJraBMi(a-iEpm!eDt=$PxwB_9PrCf#equMXDk0G{tUwb z=zb(*5v@w;L}xcZu6PrlkN>{Ssn;ijX<7o*GZm23y>HR??zWHln>AYC!OD5!iJ`L= zEAvv1s+GObe~N2UMk@gnmU%Gy^9~fTZK7`IDT~4W-qmXR6B#46j>7tNNhb^&(NtR#5J9;MgF58vCzYLXg zk=E^eE-^RruvX8}l=m7wdzGdRq#xq6$}(9KB56|3W8FQ*m$<6bShVg@X((YjB!msa zK@mLN^q?G$7^2(UIPOhxfy;hz&M3a7b6tjS$#Gt0wGc0XTBxr0=<3jh+=d}dxrP!1 z0t|)Du>dUom*u76M=gb5N{e9HgvqTIPOtTV==TPcKdspo|N(l`q0DGbE? z^c1$DlRdubA%hBp^Zru_ZR-l@*Z!&8jgNr)&I_ewLp`-Ytf-mCmUf1^P$-7&P5A$m zSfNM_NCY;L=&l5(*420PaNgekQ?hlcgZLZNuF`0NEdKRuab;>YjpBw>H4E+>>~Yx zf)IyMy)B2WKdFXqpTsYfbRC^8yUco9T%We)s<^vneYYh7P7{(crl5}O)UJ(?E|+%! ztOMQ=dK%d?^5(jn+ky89+H?#od(0SC0F{DtCVIQP@6R^i_IE-fa-nfFW&S1Il!21wI)*{a&49Uoc59pG8X zuu?PFm%$IakE*D(Zmk~glcNnw1ULKvj{1u_;{r=>Q6MC+yGgMXNL%It9W{Uc@yVRw z$2i#s9~1jPRv78=?NbBeCUKhM_gFiTp(MGHjQpch;}!k4|G`2a#I>Z&*`hd_`jb_e zG9_l&tVOtK2W&+5wxv8UC+YTR-Q(yz86G7gy_YF)lbc2i$>hs?oF)$%^Dxn9p0u>* zQ-lC~@@OXk;D{0tJM~&1(CXtsvV%F}1wxSuAAmpX3rrZ6_{Umrpn6mHNHA9@O-Y%Ey}&BZZ0O3T7Rw^{CN|`ElksQAy+R zFY`}Nm(T8U9bQMq1~%X`qU}6$7-pGu#QUuiQADGdi2~D9yP3j>u(z4<^B7A|?Y;&e zK|3=0H9(+*&>v)65SNy>vCo2HMql!bwk;dmy?(@qzR~xEG7X!%AxX3l@FO=x62Z^4 z#?Ok8_xf^LBLi%+Z4pU_&(4WI1)x_9mvwRb*rv^s($DfC%I(US4zF11x9NRBLryYF z65?~DfM5VO@d6=76!)!00+V{`;sv{l8OvB_pWNuU{L@pJ1G(3V*G=tGDmnsA^9cQa z$yoiOF`}rnlQb-@>Jb=O|3M$1#65A(L}Kj&Svo);y#L@_<6${r-^ZA|5CIdP9h;hq zDL=Bu{p4qQkdeDFu>vmAh}pFPQ-!7Q!`3`WI$Jci+`xK|m$EqTFw90io&DCPxh7EWeENb1sFC!#8)OO{=>5wDgykcpsH6;>!J#l0B;|2q!dsC97@nSGm$C?)tpR*U7aH z2St_pxl)NW&85}*0=D%H-w7BVitf>*IsHzJ)}dwvhazJj`%DVabqybSAlVF553ic& zo6jUFpimHd+qNSWq?_=*2wtv$?!^hO!Nvc!n4 zyL$3AirXjEEaWhU);5OB7UjfYT%0IOTK`kv@R?qbv15S9g;}QyDnm-ob?nc=WDXLa5y6eLW&#XLdL7X6#f4v7(k%>%P7VL-#R6drpD=ZQ3*MO%YCIie682mAp`^0w2jJj zZ+i~7)T=ix2QpVfIP}Y2}n(7<16_7QKfI>lJFeqjjDk~vgew8|S5B?K_{}ZLO zLA7;Cj6=BnRJnDe%~nUdiwG5e{&cb8X*Ph#`1IrMPa=TWF!8z z_zs`}*WnloMxaVuKzfyGkDnS^fNemyXZva3@=5UV5O|=vL~`2liAXevb&{JH42#uO z@KRww(=vNmsbt)Q6*_4^V-Z@q%d{M~$zZX0&ucgdL~q@VKq_eFL2l{Vismp|L7iV0 zC~lWuK1&b!?cG?vY50S2Xn%LTxbS7b5Yt@HX2#*p$72&}A{8WvVnYDDxc*zWssU3g%dAuMzQzReH-Kfy)ip8kVqvb9bB>wjJC$Pr3nJNhC_j- z=e=Mm1Y7<=TM873-FA51mNHP#k$?uXS4x8j;*FT{H4!VDyjFYJ$K@WGBGC#oh7`E>W0g~Wiaiw-VE?yGIVeI!QYMqiyS z7wFR)8i-u_ACPWgTGbtjgf=;_&)C5qCsAORc7EaTkowj3XfQi-NHN8Ti;Tg{y?gGU zsBACFkFwX^xq3EY0rGdL^>vpL67JIGDww*j_U}toP(h=9BSDj|r4VkEgW}v%jw>f! zJw_fvGLr2s*$Fup9izdw6p@&*qF>cDf8PTVsaUOnRk~Hnt3QhDsn$p#7t-?+6Bd5g zI*`AY`1}wj>R7&A3}zqE0)@uf-#l`8s5>pu9;bHYDkx|To7Vn57n=t3NoP=L8%eXE zHRo99sQ4hV*TBIXQcfx8*GXCq=o%)?nSQs_MOOF1fkDRSRCa7k)CJn0=QWRm(#-~6!@ux&8IksR@CZQKZ4{-y4CQX-9%&i0@ z90*5SP+VEjq?}`dJqQYndVFV_=Sy8NJ`q%6t(m>)!Y|k7RU3VhH1F5`#05O}S7w5oR3u^V;=awFY`kxgQL**Rvy<3pwUZ?1S5|gTx|;iPnu~QNafwG z`sVWLniNl=%$DDtsiJh`%-EK<2q@;YS;V8w{aKg&|4fO9H$o6X`~>N2=fw!j%wJ z>=f%&kf8>XsUJL{vF?6(QIg0#9#&SxV(zaPmHl4iFy<>v2d5ENc2_JK!27(}haq!i z#o@{9oEj3u&w|{)$(m$tZZT;pT$)B^d&a)K<)iERax4d%#uAv`LKL#86rE1~ft8xb zCmC=_5{PNdnKpe`0qWronWFHsNjzcqdVJxw-ag)=fy9V7;8dPjI~W27tAr` z9BBqccxWG7uzyHmNLv|HbvN$RF<{)&FrfXzaZf0vgT^)30Opy0&oDB(zdtK0`*&u; zOu|{i*K^IpVE9KiCaw@?3LTtAQr#of{{#s^9#8<+>30B0x6RH`6g+M=&nVtKX{EY) z@$!dioVxcJd#gd`x46}YR<(m+RRICR#66B@`iUnub-f-XuD=cn^iVmNvw3viDN{cb zv}5zgUoMe9DX_XB;}Px1MN<|~=97`fuI|t?4Csg{3qcF zQiD!ZyxLkWiMbdFjiaGq$ zbxz_d2qq}^Flb+my?H*Id{~LbsVDZ_>?aiPC}eL>3{}aokhS%oaQEzavrN>4_RCqI zI0;tWotn5{#>meX#mjwqv3cKT)I)CLA^yDv;$s47q(2>V}0#K zrkiU%oSni{dzT&3Sb$x_q>IzPk)W3SXVzEo#~@D+o?-!`pp9)4=%T=sd5Z@)`n9jG zwgig=d&Hgxps=Lbi;a>6cm1FF)2pM zcR%|MQEb%!^DU?P*57sWmvlB^Eix^EZA^LhkC-=F^#2UzPE(&W-A~EZ-IT`#$$Wjv z(F;0zho#q@^Y7C32oC6p{!yg9T2g%(z124JNF?i7*KiD1+nR?Q60@wx5<(Bz;9N{* z4tWp5=Ce1vUA*1>GGHdgvvJ8j>&2w~lM_>;K1L zPT53Yj8Z{zgtQ_d3`R?cfD%LL6zT5K-9tiZ2r3eiN_UEgQ|XWxASJO;gAMpSeBYn< zeSaUnzt6e$xOUFDUax1J`ZX$#&*q#1GR|!lW;0__r|W{@B)S;nmz(lUe7@%R(LNI= zKaAP7>oHlj*rS7nr4t)=TBft}oqmO$LI(q=4nOa%P^e!gXG-4Tn3|Uuyp*!y)dI&k~5Q>3TQsS0c0gk9(D)45?WfpB=88gx?}v>(uY)bw!2%GZiW3 z8<0&9BNpai7E-26Y7tfMz-(p(Gd@x+Rm1ITwcP-a5g}1Xk=F^Pv>>(#z}zoUvZGaV z?#iYaM*(5tI)IkvIvEWvIt5`kht1C}?(~2-4)arD%I#m=;Z;z}PibOC)X7H^;}u;V z+LkTA9rL*f?;p9Me$Vo_Df_XER=7mT1_7Qpa%HBQ$%$9^-`Ae-_Ze83h9~! zO~T7NV}q#N(1)I}YMZB#Xt6c)osZ39f>RC}2%WxW1JB6RL7y}VsDlvS;3aRiPXed` zr602YzTI=4aFaL=Zuq$lm_gp@UDPi=SfgD4Fe$$g?#dvqjhE1GGwJ{UD&aSlURyTe2IGAIVb00s5AgTUKXUTguPqYJ2~6ncGGcE|5mYKAc%S)d*{{;^Yn)R ze@pJ)4vD~)heb2N!9}>z;p}i53>Re|ou<}q#>z9^GB1csV7ChWAmX7koVPx3WCSkR1v^<6!il^D9AMillwSH`U&GMp=hXA`QL zH_wwnl5R^0dMU@Y7NUd0qca04ig*e3l!>3g02nF6(IfIN#e<2P0pc(r8El5Rzr?^n zp6*n=Oxq@EdzGT$fSTWI*(;4yD?{BhoB7;PHDhe|Z3Wu1I4adR9unp~|Hx~TT8|16 zyISrInQO8e_gz0ju9MC2^&uXiy)U9y*Xr$(M&JIbnwZ;tGJ0Y_9`m-AofqlX9TcO8 zy>J2~aEM(2f6b!6ttn};8?|`2qbHfDsk7MoK%(}IVx({gLRYY7y?d4IXvI=(`p(c= z!g$Q?pWXXDU3-E3bVKUX7lF$6X@{{+TCHcLsG_E19#mo8${dWkAeHr&HTZ`?`}jUL(#Td|YfvT8@t7>-12zPrBY6r8zq1A4f7E$i7mKmbJ zmg*DVuKMSs`| zQw~YS4{DmfjOxESUjY$J)#y>UC9H%Dn4iBw9OLo!!P2ddY0+z>py5ds4!COo$+vuAT`8yWf^=g$A z4owdO+N)c-#Gf|2i>m7Ay&KenV(YQ6c6QT(1rMH^orD+;VfSOz<}AToE&JKL)!$OJo^={}UhGYqY2yr_Z+==p^Ul3C zutS;<9XbuqS=b=##F--}dO)%g(W&L>j-KS_nxwM{>+TfZJN`o0AA$9rs+k2Jb-^wQ zRu;5&Iz-`EO}$~sJ|C|cPcd_Bo-k!U*7oEni2yRNw<(_7t5*Y<@f8s_2!G^`R-r_7 z^m4{x;USTm&;8DF|ACVxtu7%RMyE|^fdyq}o@TQsc{_ns! zI%vVOT5fl8m{+=gpYb2zkcUB!oRswClAK~!tRofQG*?Oig*8xnAO_ZWTfebE0Jm0& zYf&$0D)5;5ka#(Ejjkt-Py};(Y>3TzmI}c3mwpwd!_g=mW=#aW#X3 zL=IJKv!<_reviy{qNaJ?O6V{)s`eeH9>k@U27>4vh)!gmsGL8Cw|$JYED-^6x)`o{ zX?Dmh?zZD%_pcmo@o07!n0CIWi)kYID3KSLHF}U4{V8V_fx%3IsDZ(sxdRUr@e&GI z6Jx;uxN5)>*CM5uG&5Mk(gV;oSAPuXu5XEBkD|>=d6H=Ow*0cbdn$}h^up;uHrvZ~gl(%)$WV#KR~m_*k-7Lq7rd1^$w7G093k?g;<;wHk^5YKEz1BHQeTnE*)0{Uf&XxCyTa7)F787J8fd#{;*#HS$ zSN?c)pGJP}Qa*SM6BFQ$-37(Iz|sXdgOZc-SSO&Wvm2U6!j!b?On*FC z)31Nk+OH8^w>6J+3CrAi5XrWEPFsjPQoLrbMtc2@-2yc(RuP~oq(WhqwetP(V9j$k zkWe4>Ys~bl@`3bbS44ywSSgB{waTSwbm?lWjK7t~Pz8a^iLQAG2}#+9IDv{#(v8t` z^D)U4jR<7Xbp{d|6ctvalw`$JVrKfwo%&66Zx!{RxX1cc`P{MPvO7V*@mZxc!TxJYbGm`Koc~H=~#Ado_L1gTR3?5^!?Fo zi(d?C?tHgufBXOmpwyRF^#5I>dzdhs1HntfTORsV;SKPS;z12v`PEs(3H!sw%fc~H za)X&aIdw+uIx8m_5yL&dHJ@;HT}pTkcLqrIj5CJKs=9z7^SSNRF)dZwjMI0(8b!Up zN7~A&@FUHFIV;-a8!fofJ5R=&Q;eh;Jg@yzFL@U)l#LzcJ+u`8KKxztWffZdV8b12 zmtPK*stS&Z(%aTl;2I_L9FPd!|9|I;#Zu@Gvl}X_(o zPk3w}inqb!tjM>tiO`lmdhqs(?Azd6Pf(`>3{Pp7}7{kYT*Lde5lXOSnqpOt)IX9(HAxt6QKs-dx`DJ&cDp zjN-)WZ&T!r-(u#a7YYc5uviFMwG2&;KGU%MGBv?_qJ%v;)8R_`jIH-Vdui`*pmR~J zZBrQ;ah?ku3$Y$B3JS0x_|jQQ&yK5n^0~u}>ZQ)I5j!b;$48nRkUyTaEFKY72q|HPHfcnYIG&T2u4box zA(}U-Nf)BaY6f>)fdy)SCp<;OS2s7G-t!);yQ@0(-i>LJ;$Z_-DkrK*$%|(#x0_~B zLbGY3K0*qN_fmwiv%{18-#=^>K)utMJBp#_j0N_u1eo(ck@d4>ATDJZ3MuA%@Ajo(kTvBxaB)Q0XfN_XUR7#-9))a*=xabqQRV_R| z9khS4DeI_6MehP{@~6OCc7-fjFw1bf9^7P_fVs9b>Uch+cOMIP0#-a`;qMk~&}mkp zb1J2L;`&dc@J>+dBrXNSvfg|vO##4Oh2N~YjaeEv3f0`1eo)b5qM7YfN4;@}vScIGC^&1fubx+Jr}9bN|2BR^#F z`*72S_ZnqyGKTd~*)EN!ly92160jQcWs^bKx*C?VZUR;TE$zyER-?e!6*vEq1Xt+r zXF<}%gr!xZsHB~xUf>$SnhP}~+gJ1Y7jn;Qiyss;NxG}t@;T5o{l5#Lkb2AWDY4zkZyOZ|O_rM7 zrz&N_2zY7DRlIY6FB+S<#-12J{YfH$Xmvqj4b+dxyI$o7dZk4+2wA8FG4);9aWh;Z zbMOUNLXW1d$zhhKd!wk$4l7sv`>tkH(lJ`~d9d}8E+v*xC)430iIvP^0c&r>=tPTS zLmoBtn!v;j0STBgT$PT^+;z+|(?#(Gi)IaMV83qlENj+Hh2<;XMj-oAZ}0xPI0uLV zoX`8_EaX*@<2`e!i_IV5s^zEaV#?IDW6%7%7X8jz2;%P*SbfhCj7pJq@jtf!;r=#} z!X~-5b2j`0>3&#kkJ&44YdC>wIKqY3orBFQ-G%!>Dv9ZYeNpCgz}1u(HoqE?W)U}2YUP~lk=Eg4kj?py1*ggP&%Lr@tB zi{G{H!z#0p`sqS&0V!!JU0jdzo?>Wf{w2B#s0azCEAVBDffbU4GlD(^7+vLC}@ zzB#CtyYA8U`8a{Yba;>5-@;>wed3z{F1Ev+qs#8v@U`^uCh5KN7^Va^301vbnlmA_ zN`tE4ahaHc)I2}8r2w6e(%0*Z6OMr>hfXo-$fmG*huAgYT{<`q3rA z{yTLx=^m+$s8I2JiAK#h)l~?!d53Ov^D=j=VKFxQ%AGt0yXbLYQ*2D8lB-+$AZ=@S zeYJf~A_a4y6)u)ZGU`cZ-mG#yvVbR-ZrNt_#rKml@NQtJ!!Cm~N$@9~ki*>6sqwqO z*(EER^sXkb!H-|>S-1@1i?j|5C?O9i;I4!TeoF8G)Y5_$a0M?>6%p2#dF#0Koq|e5 z-#cK;V*8h$;(!E9jLy{6*2GVj7oHiVby%2wHP4I6qYnx@w+$P@@MR03#LMWs=+{3T zd{^Jy1>Uh-pb^r}8N}BQaQBuQ86wk_`?x%j@jZSyoR{M`zHW}6fZzk4H<_C0Vm<)s zUOX^U?NK@4j1oVoPjy7fu^P*86pDr`0R^{oR}1g2=Tv+;^~Y-{0vPzjUGcqg{JWe; zojBse7=VRYd3xc~RAKGAl-lt#Bl5?fUlC8iHaD-2NA1Nt_l9kz@xq0`ut*}&X z7VAAnUms^+j7jLn(FETnks4;P{}B{7OM?#u5jfm(3^JnPwaJ7LCr|qt_hX z`UG?UUnnW!pCmfgFsOh8P;K{G9u+N|E-NceR)Yo+G=Qdi>08?iq?07!=S*L(g`S8o%@Ssr8<6J)PuZn?{+3Ig#%^B7qWZT19Vs&c z^m)4CBECL4R_mVSZ<55)qZc z2xs55>(BzI$4iP!)uIJ_`U0Y}WBW(wC+{uN<|bF1qn9c8hE5@-k+0c{-a%F7ZAgze!|lvA_?yB-lrp1XVa3Y&>u;R`XS^o2 z!3N;-$iuiIpsF*kqaZ73$R|q8`MZbl%i1H)K>Qni>?v=JDIfu}iq1O=m>Y}h42@N5 zsY^TQI5t2Rd#H4d#~q5Q7a>Z&Sr5{Y+BPRSk0VznOiK525XE2UJ+R#2O2Cg{fo2+( zWT5{Y8$Fp?RRy)@mM2_yZ;KIB0laFe)w8VkcOX)#H&+!>`lG$60pe7g%x}M)#y`wcK9U@iSQ2@rcZzJl2y<`L7V*BD%Hg984rz< zHVZP_%au054X(6W2p1!*Xc;>Z#N|bX;8kEx&IfNJ{ZX%?YQBTmD#e8<9?$Ee*=_Jk z5~l~Ngc~+XaCFT4JGMqOdc;b_Y%SYy0%0GMxo}bX=4IEWYDlntgHFWrk`ThQ0dCEa znl1M7hwRauQm5bJwV-IVBtOC3UI|P}rjH`N{w``%AunHQ?qidtSG~Q8%_sChF-%uL z>}SsJc=Uiy%VcmWX-Zt;lyjJ_!g!^_O3lef^HB_kx7*ScQdON`OcCdO0qHODiHTjU z+KA?fru!)0_}{^%^n!_uzk^)iIlFGpplOde6sG6v`%dXyoB*tWquBGGTSr$Stp*SIMcqc3j4q?Q5(a`|-Yx+d5hJe@&GRqb=M-&7T1V2P%ss>} z_sez*-g{+=`8NwdADt&HbPopFHtV+Th<){gDxQO!V?w`duX`40if!s%j{HhPFG{rOAx=k5+Hq7IFW8}Hhaqo1Mo*QrHN)>+iN~1Nla=QnA>!wO zWf=_%4RJ)1_Z?WK3s3eicvpEb(ec|wk$h$ta7Kj!-{pl?6RO;6TO?Rte6RZ+!&?~L zaGrgt-$zG6DHtA+AxyMev?Tv4@tEqD3X7K&yDl@&?L20_W8%Hs{H=JclhF-$YG{&! zOzNHKsrQ+*=Hh-%859nA^ieutbq!ZHNmYIINNMRxEI+s#;vQ6@D}0ap;}pZa^(LuA z4rcS(cgaG*{z2!7H)Wyyx?X%c(V9C**Y5|pd84skMw0Vz?LonjcY^BE(=&_A@hW{; zR`v%=YYIMR}cnTIGA# z$Hq<@xk;LY+O1uBE8S3T9zDjM5T<$b9ZH1`A{Y(y?m9NIG0evz!sD>2^Z#J3j zn{JA<>Ft~L7UV_WKiYxVCHG79T`^}sM(hAQEe`|##* z!$Ou?r;aYNAyqR$SuL`p3L@B11}sA*WIK6(%V|ZixH+<$hES z(d0cxyU^2*-RG=FQ2O(Xk=)2WI#(3ZD6T~%{1R#;vQKwc#hJ?$u3AH``H@e+^cVLP za;q};#P!@k<}cRkg|xR>epX2ySNb--{o(#a?2!Qgn${%%42;}d6pYrENz7`u8|zZ* zo#8Q%^x0=qszeUx79}*?UdGoKm?S4)UcI*P?45%(|FEJRERX!$m3ggRN=ThZ<;9@d zSA%^=pttWYzPrE!jrr#r5Y}E1QS7U^sjBhlE1?ogKDp8BYgI^dc?Bibd_hFUi+@C0}TkZ@#jj9#{B()njL+vE5*{d#ceFxwikReT% zx!F9*6xY+}09kPy!XA^EzFEQg{t^Eud}ApTh0sjW^`+W{S3t2Vh1ttc#2@U^eY^AO zA>B|W@g7C03y0A(CZkBdp$rWn)__!8gk>nYj=m~mSgN?=d5v6x z+(kCkd|ATk+%V$k=!w%Ch+w@W>JD#$I5%Cz-8FVexix#)M(sZ?83W)?q#ZuV;Vu?d zI3&E2tU@-R4DZ0NXcESo$L?u0{u~x+3;ZMiL$9x zCL{k(L#^l5lV<^cjx>%_13-}g%6=@=GGP57D2W?ID`KSuE=dOUY|}`?m0eRS1W*HN zKLh`h3FZYt;K(3<1zdtldKfTEQ4wDcM6$~=0RGsNyY8Kd`WeQ?&8SSd6^}0l#;%_X zqou7(3yjD~iEj@7=j7d>*CT%gwT=8YrTov%1(n5p(dyzqLOy${u81G41N+GQH;b$Y z80OP!p5;M-V0Ml-DEoDX`Nozs{`a_WB1QUv2HWuQrC(1z3CDawXyTucZr4TrJMNuB zaX%zQOZf}%R}Ehfm*oam2vzghl$Wz37iGULHUBu*zj3+mR{`x=Ag|MhD6QJlnl-A| z0fd#j`e+k~9{FoTi;HL`g_V4xw(_G(#_d^J*yVpbgQ7B^MojYR1MR*Q2Z%QIl_u(Y zh9ZjaVWwvcEP#5KGIu3gDGYvvv=!QV&9m@7_NWf8yEB>rZEA*oWj;0*e((K4W<}mf z#?5<<+S}zghq696iWw*_Z+!kO_qD^FOAWXGd1@3EwlyALw|qvg`B~B-Jq$RYsEC)i z5@&SB17J8cl${w>%Ha0>%KxpI1Yg)HRij(42-ct5xx);|h}h1>Hl(QIM<-tSnEo&5 zodEogLpIFUWv?q0(z4_=4U+dr0!r!D>7q$Y*^Ij-Hc?K7JPb_=Lw;K9XrTuR!uXg~gd zCuoc>Yivn&R~@LPar3RFdxdmSUmZkh7sfF&k|z-)ObPy)(lw!}khAg~R$)I$^DL{2 z53sL^%3rDFRjo$mYKH|YFpl1T=to--rd8V1_@ZUeVLBJ*kaghl3;=z8Gdz#QxMcR; zhvlQjabRJ7RJL@bQkFfGv-6q}h`V)Z)$$db(m!MDWnF9*X6RD2Hgy7~PVeqfKb2T2=z*OLHLKziFv}+4xprgxMfe={O}|W*$c0N)H3x zR8+)|hG}kEvM?b=3cP!eJSYLui^EqlsxJ;--P}Btd_yc-$e-c?YZP?=W8yiC=B9;% z979@Nu{R$r+H3Bl*|t(H>yhtyc~)q=J2+p5@!<>S;XUm0uvE#^p9f8o#k~ExBJa8% zB`Jm(=Kex2G8Z8ZKwWTKZeUD22E!W=g1x$>*1u38}y?k1hIYgzGoianu_-0x%{a#VL9$&~>2~WTnnO&iJ zX{z(#=F7-*#R9MZY7;JZ^dUP8H@Nkh7RZS3$%%G7Qo@gp1o(|X)4FoN0;qv&W1D>H zMTF#Ye<-R!!;%@uh^Q}e&4cKX{crtas9mD$$6n`r+HU|_ys5|@RiCj}uLh{+EF!+T z>@#aXzISocG87zS{HtEJwb_>TA-9~|`6ovc{FA)08lFT*QxJMzOH~A&ZUPgh1|(oi z^cr>gb?d7s&08v9@2CXB?4@48?(qup$J;U~N#w;$OIH$Kov}?t{s2R=(5x(^P+@4! zWy*f6@cY?!xYFiHMc!a!7by^Ly4N3cx^JR3K~k#i(&?mXK(Ye<|NHvFk>%I^Xj{A? zam?mh4ppT>@ZhG3D~EP0SO6spKcO`PK#_MT`?2WsfSw$X0BQi}sWrY=oLnJA3jRoZ z2tG}-FMnRUbE9w@KK9P1U)SYbPl|>jfQGY(=-37O^f563^IJ!_%4l$NQ))C`CD{7# zb;?aPjsuqG-B<|NL&C&$00mG)g>f~CI5#&}xD04p&3b0vd3Ho1 zPdi{jT+zv6LByrlg|j4yZ!)%Lr-#cg`1ber7Zz%)dJ!ofdIAmJ5X%PPQ;Db0#r8!L zO?MSrM3x%h5?BD$CjPeuCp{{9g5w{GMYF+W3!_u#UQ~R5AjHf&QD<^k`Qy2*`=>R% zv~s5hqq?ELK24g-RoTB^v&ewk=QB+diS}ci`CdD5RZ43UHepzuIxjTlz%tYvjnJ;M zLx*a4K15rHh>L*I2l<-a(B|2PUg%)hP~M=-TSG-ydA|Kv`+_Fn6|6PIL)b{t3Xp&S zUup;hYZMs*Tli24uU5F0bGx}wg{`QwQ)y$|+IJPYkb#X-3<@VBg3wgRTzS2`>8G=) zPk!jfJvG@o5O9>{Jruq9u@19Qjjr6FN5m!i@dYTNoxXJ3jJQhv1sJW99hyuaO$UAP zNEPeZEYy^$wA2a+{QzVdpKD*aD7gr2j|j{T{HF=NaBstvUdgazfpLZpK=jC!mgS5H zp|ETLlu?wMCO^Z__Eeo;RPsJ~F0*eY4sd$le@40TeKK&fGQNpgGS}AY-keMw6C@yOa*t&3QoH2`k10;`AsHAi61iZ(JQoX zAN4uenc-wq=@ai~)`UDk2k{2@p|RN*KP|!=n8iBl7_;Vl=E^a;Qt&KP3py8J0xM1_ z(LVo*&>>8F&JAb4+d!YH`2y^rNL;SpW9VscKy3}CaE0CO4~fRnE%>}!`!xIcWOo9_ zccio=Jq*aFT{MbwINTgJcActOS}Wl$%*$JjUdr0v-+$)uEe^NV0z>HZ9P3kW?-dVd zgVlzy?^obe*%V^^4q4eY=P7oPU02MKu1|=e{O^Q5^YDutDM+wv8UbZPB38Z-5XnNn zG7{ZSs<~x4o~d@H_p7;b;UihujbNsGQroP5Zi5OwWWXhd+;~rnPEd}oRQiT3vV!_A zUf$vKt$Ad%L{f#S{@O}FZhc^--2VuRWeneMjJ|ArO~2P+SH#6@WKL|TB%`f}oJ3Uq zN}m>vC){U)gkf)6gFRv@I474_Vm}TphzP-oh$dW$y4hhsS+$~J+}hf#YwPywNmDNx zp<QDeC|`Q9HTTD80Eht+-?!6*nU?L_+6huh6X4N21%LhA=NL->hst zpu|V*Eqhhk#;Kjky>IVFl1@NCMg)%{A#H|Uro1vK&7zw-+!4B7M?5h>Q)OqToHI>^ zd)2Vv>qn$!gI%iIk7R$7_p!$0xtwxWx6u<_hw)z;#nL`OJaM1p&<6U9&Fm+$?^(}0 z4wdz+riAr$A?}E`W6eHdOCX zk0DJu0b_UZlU+WuwiJ9U(f7oyTPt$*W(XOH?$qcBXs4tLJf^@O(=jJP4r{PrC+@E` z6#Ce;1OZYi45U&eKhG8a+vUZY7j}VSe<2DyB~-iOJkmR^g4T_>$z!V%%B` zlW{-xKuoJ2OU$vMR9x}4Xvy2TU-run+QICXOlk|bArSzO%Hyx=t z*ApD3yB}ENA6x{YQdlnRJ!d;lA#w$>aYvr13r85a#y9=2vMY-aBD*yg@#5&Jf zZ{(SL5{;6YJIxUjjT4=p-0)Sf0LlpTebhmjfB{ofe|dh?SE3s`slbLC@zC|RSkHp z>DB)XDJ?=a&$oG-0NjxQzh!1#KQcXbq(ZWaShB@&kux?3v|Tii5g`Oz z-;m%^6wqe3ZkKL<<=v+)_dxOQ0@Y9NwC9RP4Z^zv#?o0Or zE%hIU1>O3w{hrnD-Vu~uoN{Sw*H_yT7{y-Y_$NU1BZMi%=|v84=~muz-&8>*sC#E5 zN0n2fHlg7I78GX)V2h4dsjc^y4YL z9+|98cJA2!CdaX#fg!K>040#^fj$$JCSKo^sBRtPXXq-tVu|#K=TPn@Jnq-NCZ@?~ z^vJ`cjJ)ulg<%|zxIvQl2M+>@*2~5}_^f<0z=i^adu+x6R%A?9MK_$l_+ACRN~Nq6 zh#uM7w)mOU!><#>@3>1}vCQCyiD9@0c!>{AA3ARxlZ7#Gu_i<4*VReft9rgJHmH1Z zok62tKep1LT76px_2S+QUpresQFc{j%!Fi$J^o1_iC0Ms}5 z{@0O)IZti$Rae1mQ_rE82MUCA7--I5!1ml zvpnCtRW*m_#m$q@E16cVE+t240x$e-LLgRS`xVP^VGrbWO*Z4%5%Pcp%tmbkFIc1K z1jvX;z?hizyRabQZYV!?^DOyu``x{qaVBY!@AUE$@N7s%&17oYaRvlo_;@rODkBzV zp9j6lrrrGZ9gqm{;Q2NcE=IIgFqvhFOJHD6&`#hKO zV4lPVuW>`;ZSNmyEb@UURN4s&x4%{P|)OUL!G!16NX>t4cK zWwq=17N*lzOi;O!LED=mOKZBfmtGrZuUELSi>sc~4OM&XqMxlTB&tPSgm|8$MZGkI zuP|`mSVv^NURS;x^htl z6Izv3eNWZ~7wegzWKz?n=Cm~;wC6d{)t)*=(ObkiSfj3|+6fQWn5OL))ZiCFy_rXJ zha0`>$_?4uL!>Vkl!#se0Y;VGS9>gQ#)V0Q?;>BwHT4@{@AYGe7lsu< zuAw6(aj)jRVpbh^68J4Z?7yZ45TVPJ^sgfY{9m9KvD&i%$YK-JO0CNzmFx>=-10OH#dZ~c- z`)$00wDj>%b91xc%xnUGI~1`s@qzSdc6KS2<2(r)vg5^X9wf30T)w@(zmLhqU?#U+ z;4&3Yo3OMklSWmJ%Pu1`n-3|B2aNfqdBGEw!V*m`R_z-iahNB=!|>1t*EwPSOBa^4 z%!Y%@1obh6KFE$Gqi|T<4WL#vvDu;arTf<#yb$Hpqk-nJ@>Od=%kxfGnCWKH;WoIA7ad6WOg{MHK34x#5|*84kt`SF0`?0qPyF*Hc?)0zVt%?K8Y37NLbKAV== zEZ4uKGq?YQC)n3633y(CG!o1fQ|)4iiNlD&0zw?~|9-wRI6O{jhUTv zKnHgqt!jXVSsay*{xB8SSge3tLjwdf4Yexjl4}NJ1bxZQHGrR;6K(z_yJ}kPM z9dt`NfH`mc_)$FOiLSg4$*{u6PzgA2_eq7(htrc@_M;k$ynv2_w_+|K{Y=JPKF)T_ z1Id%OJo@NeW~GaXH)ffv)r4SSaWZe3yyV{jMg474CEt~I%oPHrLZtmSea5< zTG`m9;C}m`eXCTO9*`3qDWStCs^4X}<5pC6(OFpLD%Fzm9`I-k5=Nm-t(k7|ID(bz zF#SyBJFrsc&$Fi~tFd^v$WY`kJIB$9USK~R_pZHWp+10Je4dVpHcpNCxg~+6|K`EE zHJIBW-!1|e?3mAKT2UxNS*cKY#Y&d_W8Gam?Udpg$Xko-?drQFZ`rTgzE-N;zWny= zGH>0D9!jBWV*8*Bj-xO|at+5irP>q(7N&C!ytfp{H^T}pnU+(90DzEint7w6EA+m( zVW2d-A3fAR9w z*bat6nqggDWMDsbB!g^LEA_)d*yP}H#qsvlia};2?`jxGB8pq|EDH?PBR_;x8S3U! zu)c^dQvTT3@PY7%t&m1IG3+0(;CKzlh_DHJ&h&&gjM)7pQn6f{i(s2M9BX+E$i+zv zd#eIuMBK`CjuoT?SLP^|_F*Zh*xZj#P6CXi2Ol@gc@F>iG-6Wae^; zoa^L17IE=y(kyoC{JC9k49x;!E#C{|g}v%YE3+8pvbMHna-a9A;qTwStNtGP8G9Nd za*}U=zd;Bni5nHj@aj>^TGMN*j%91)&1}Q@O?o}@*z>!85`ZwhAV2{W z;WSYhus=7)CMsleI}X#jaJJ)yZ1vraEf1r5z#g!|^X>jxKFc|drD-kI=r2mfrG}Di zj54I7XWGX8!fZ+D zN=wQX+i`OHFp3lX8fWCNahETxBtHhtDK3LqoGoC=y222|%S zBHkz%*GXZsK|kroCT}Rn548po;wvq);D!JOem&l7@E+eV)?@INU$z@+ zKM!`dQrX_=_1rHD z+y0Y7+HY%ta2oL5Sf_i~^B0K!1HZ6;p_R&R&;sdv(1v|KM@N5xS%`x)HMn#5sXAnv zh&ewfJ?=e~5FOUF3n>@Y169zlx{mRRAQE*_Fxr;BK-#?kc{yUkpF)o6@ZSvYjnWGX zR+2|JQh46q4}X-1L64A!l3x;1I$_iN%W&<>gS@a%zW$~e!Xsw@n>Qa}!nPa|yc z3XIkDSfvYn0d{D9*tE53N5cQThicK6<;Xq!G@R?RQ7ioKmU`@02GT21eDLupGtc<>bN>0s zN%CwS&Y^{++0|`y)x~L;Uk}XjLOlPFa6j;O6i5KY2b_4p%NcG2)gvFoVI-X<*t}LV zjpsiTygqZq!q*CRgV&HEhzg?*GqxwM^72@x3H4UJF|iGQt{RX(Ix9bu&DSDY((p4y zFI7I1{h`C2g)1GZcUa)%40mMs53y{RG!MGvyP9dNkgPWvh3Po_;C420i4MNEyKp+u zh{}#P*(}5;xw_4|b)Fq*PRZ?2d3kx|rQTiI-`~He_1&JyPV#h)OwlLbdtN6yGyZ~3 zw1+D7+VQFPf%1D&4HLo#!$sn!1dqjs;Y#^uW*IQVz7K!vQvHjFhFRCvEsnh~s>+}h z9**YQ8{cI5bkWT`sc_Fck%lSj;lIS&!noo=Sz;~AaSVMi_>noPH zI2X&eK=r`1%8&He>@XngI~0(B0mrnkL-&e!v%e+8z9yR7Q5H%I1HvrPPpbcMIkmZp zi27vV)xaBTwFwxHpC3iR8b$TMk7RG(&1aH(zcOlu(Cw5lW6tAa6RzTazx?@)M}D%X zRQV&~ynUWagU8EPCXKGY3Kxh2((IwDnZ`RK9Yr4+W&}ywjSF6r)fN}Oy|D7fftxZ1 z-&HUkryHf1Ce+#KwOX=smhB|0vFCEtM^XuFg|hs#)(0(jyh6!+x_puJbMVTol7BA# z7FEDlE}BSzvf`s6Y{j_J=!}^kTqD^YK{|d1fSjK-t->fN*T!GI*{&TYzdIG*Bt4dL zooiZ^oHX?kf@XS>@AhM9H4AfB=rRl_=^kkje!#Z05l0URewqy;ouRjf_SH9R{%_Bo z&KWY_&=pY9K-w-|jBAHUtPU-m{=AwOBv(!_}~B+gl8~q z0QOYco?(P7-s&NZe93^I)EyVN-V?rf=lPvSW?I3gK|;u~5%>I2sl^`5;yJtx=d2J4 zI*Y(t6*+9OUFj!J_m0KexhDkX9j`g~FFa{)fF2jP3OStUYd;9nF_ox21 zTo&rr8djdFv@>OnJ~HH2AB(bc@O-&JTU9e3L6nb9UlufXiE!q3@tsjeJZHjMZd9Kd*XvuaiSw#9zdY$mR z*VWfGE|-D0dWltj4JPv8m4V0iXT1kxPK070ld;SsA@t1Ug8(Y6wAep}lfV!(WBKWF z&R5e2bexaXadihEiNaVauY3#2dxT#7Y76f$1d=FLruh$I2%vwWnK#k^D(yw6rqd(P zVtP|>>cZIPF6!+^D6RT*kPz~B>Y+dc0Q`+ipl7EqcRq5E!PhFal^v4+Jjio8r{|qA zp!s9zzl-C$)KM{vR5MFiX?8i7+v6%|y@rEqcw5R)`%dVSGFse>zX;K-? zDHcLWg^NT;6B5<=ciHfaF@Sh1~35zMc+-kZ)pK|;t;L~WqWo#P$_bFWPp zUG4s9-8m7m;3oc5s|S@5#9F8s>)Tz|*&Z+Oa8X?d)P3=f;)#S86Z<7Hm`8>xh%Z;` zpci@`k*GDY_=C7Hh#G=m)DPAw9M*@co0vf-PXege1YBd<)sU+(IE241hv!q-ug%0* zUw${!@LH;Qo_WK=(S$@<{W1mDZ>9rZJQ3R+n@<)_85_f#zA*s34g^rKg08LFijTO| z?zuDd1IdniPDhGF6X-a!S-9|r@ZLulX2f?K|A6J8UHMEKp=h2J0n|!VaqK%FcR113 zx*iRWLNi8PIK>`5KoaGvje^fLm*2S&U;mVyxkRPWpK8)SfSAblojP!}a1h*#9TN%d z9897ZDrfVr= =Upw|l5fArC_9r`c--8^?9Ewxivke=F=NSt6L^eJKoO}7rp@}U z4v*itH@^KTQ?jGdJ{8qhen)}SwkW1|hV1)abu-w`0xrgafd}k$L-?Q&glcVZ_D(!M@XJ}3zZ1M3Sa}`s@4lE^y#o%t2N#y*bZQTt%m@pv?ayRX@q-hJ=jRy*T3HFmko*H+TJf+{~>w# zHT=n;gwyR;o)dJr!$EK*S0IU^%w192CvZlg^$I3J5i@wBOIf4yI$J-Oh|cG3NU3Xp zHxA416*Oa&FYh@wZ2w)Q{1YM;cQ|lZ{;#r*8#)<%j>B8TK4XmB*%!u9c$_ZM_0kyly2RAx*)I!~8JOLE1gJP5k-*`YXXURW%NyoX>xd!_n z`~ub(sm=lqY_Q>TMq=hHzXN}1y>O9Srp?oiXUIz|`DZ=yk$Q`%eO-W6#_%J8T?)X@ zRb8c0fhdiy*)eitnZj(omg#a;GIGeg?hx~5AXR4g3pU*WX{nX6^W8zqFy~>d-O0j-q;@>)sEC&AV1txhqun3c={50Hm;&`Q)4uJ z*9^JoA3f}9=+sa?U!mRQcHLx@MCmqm8Qt9pVmu)DSz%5r&L)S89;EUjzch$6Fw;)T zE1$lqGIf1)>-KCh$lb8%(D@=#rJ$2b#P>SxyC&B+EU_*@BucJRWi$XZEsqb-EEN5Qo!44e>b zVSPSPBPVXA&*(Z4kjL0xmj3!0D?Bay_pf5;Qv#l$@u>Mlb^h-|+9A$i-5+2{inqcc ztKQ)9hXi^yLU2myG4#yoR`}=WW^d6)I^N6y_^%IbuuB70@V1uR-O&)o6M0y%x#_|&{Pec7iUAyQpRLS4X zFi9oQaY3P9mdjM*?+PIIhNa(7T`*jV1?arrHK)bBwe?$^el03-#HaZr<@cd!95El- zxo7(2Wyj+R#C8EQmXB+mhE|7cYpT0PTEo5%Cz+3$X>H`xGx1in=BWpQe zuXQsWcln#-nXgs~#7ee^20s>|fZ$4|!1v9x-xx)MrTROkyO(PHk|t*C#`DMDUuG;; zuNFHjs7f$&%B=C8;-fd;h)dXhTvCzey>FF#!11^hTbUc87%H+dkz@i09<@&pSsL9W z4>o?k)TPnz2rUqwq~!{JUx{Fg2^z&1{Vo2Vx$qxb;Wf5nSxOv}yzdH`yB$z|8v}&? z-J{k*(sLQUopvojrs}3Q)H;+4u=_`U^%cFuKe|!flp`h&zZjx`;euu?`SysVFQ6jj$n%f0V(0D=%qrx>1_NlDXi$LKK~Oat<{l?P?)BU}TUgU_#* zAwI)sQ^q36#Z@0+0I)#@10;leMmf(@3<6e%nSGQrwYp=Z0oIgl?Ke9A!8f8fJu|>0 z<`TTdw|QQ~Y;`0p?OH56q57D1Rk^aW;PKPDfiq-Hh1QK0vxJLm{hFu_pwd2PH}M%q zbDgjTX`S(GPAHJ^{qpLA!SF83mMl2BSH4SP@fXZk@3OTYCgxCr_mAR6Yw?!OZKEX? zm2YLa1j=-Y>5;tBz!AZtSQMj+DdWRsB!*fMe<2`#|D(7rVnp)cmZ?XQE z#CS-4X{qA3Kca~B_s|#XT@pUtJ@7~quYO3x?&#!&pK^8b zwd7P6PaB~a7DF{9xW5XUq5s@x@5(!R&S?$4fO%Iz%PZ6Ux3<|JLjoj(6iK@NWc?=a zVQoVm>(}pvzdFX9MK>ih@|jfXY@Q-6lV`7%S`_jA+cv2~0Z5{_f2qo>C;rp%m{no+ z@2d%*+H)vI*E4_LST)JT=F)|UQ4HUqh@DFFnto(!uU=pZIOJFydn*i(L@_lDoZvC( zc(9ku5(6Xc;9|rn!KUdy7Zi%Scs#;68}9TwT3G!7(K>YZ7%?-U$rvn+cO?yd;FJur z1B;a9`5gA(b25RB>sE2v;QS8*6r<}c=H5s4;06k{(~Xj|r5=jWRdM8A9S4W!Pz;Mm z|KdZOHM%H9SMHQ=F=veq;krjqnoD_A-{&xqXg0_JKoX@JTf%MsuxS;;Jjo;`kp9K{ zb#(w0+a~p6M8RPA2V4s7Z^8D?|Eno4PbAQ@spOfCg9t*~mn2L(wshfze&An(LuInP z9<5+s{65exBM}X_jmG0Yuh0KQxqr@EPn`nZsvJbNLNKN&w)v;we>J}P=iX&9MCqsd z7s=*%pT}J8+47pHx67;AKi6=_{U6n$3~*=~OdK0q3jQ!q;t%$DOxVkxmUM$hmi;zu zcKnvKH;|(Zj6g!j%gMJ}I5U!LIFfkZu(>AVDw~~BJfFz12Rk*ryEZuB27-i;11aA6 z@;mLncN+O{X2>8RWLl~(j{o1P&(?>D78r1Ki+oY5nO$WqV`^GPgCjBK*ci|b#+|k!RvTYz|3T8Ld&$hOf zO+TFb>$ZXMV*(v_(!QV~3G`#e`+ST=jAluaPH_)gZ0$|fuwIqt^p2|FI8uumWfK|s zh|lRM$QSG7=MTBM5UC4flU>A@|EY^3?JQF_&*jlBir97w&V z&!Iv$D0KBhkBwen%5Tdb4eWQ#v%L~rp~%!ED3q!9aVAnqoK^9+BH0BJLMkMppD6hP zLn#(yf%$-;k{U3O@QhASmDxncN> zynJHFkN)E3jvye3@{9IdB5@yJ^^fHS-t$4(Um~TNN}y+t2wq>4(sNpaABxZ5AyLAv zRMPnb#X9@9Fzu)N?4A?>pKvhpi5*PscK3b;D-MpScs5;<7KifYO`OP9_jbXul}Gtw z0zLbb#T1IOGl@XYHt%x5ab_M6=-DF6lhT}-X9Rk-n~G})XNEwaXYV3U3UFqk2=r_s z*)@SZqnbvbXHThI-{uV1N-AmDwTLrFCeX9LcHt(n(S#tRM&bg_u>AHbxqq-f^C?!t zlyjvRTjMx}Fn4o4qB>&g+es~7cZ8)oU zJW1dTvQ--sVc^wg9N?7s*CTSU45kf48#^xmupg4v50c`pVLz3I$Y4&*e$(*eEJ0*2lVtG6I5}Puwux$glj)#NoHLr7iR-zx zA37-spkmb~2F%&gl_&$C(u^7Xe%#=wv2sH{TV2AJjIo0;v{tbu&_p}< zm-zhM%!(Kf{g>^Ha6hhKP(WyoIDm@XFGW>k4<@+)%m224C)PsDSl@2z%|@~Z?rc%N znlAE^J+NiZ+&DY?ls))gXd;rs9{dq7V|~LZ&W5oEiV!oFk6WckJ_iu?%r)4o-c@k1 zTS_W2yV~tdiTCvuoDPbia%{emV>Y_jT0b4ngWL`LzE_z3q`2oG*uY5@C@;LI`_ZrA ze@G#Q37WBtg-iCAr#8)F6yE1*0|Xz_2#dxw;lQ@ z#wbFZ7$?{?FsFslP9Iy0yM){S7uLyQ8O$Wm#hKZo#5uBk+}Jdnbu0QmWBf1TPQ`E? ztGW|n*Z*;_-|fHs0YDOk&$v`4cITT5@`qxEP4-!DdZccnW&bB7kMF3Rn%&Ttz3<@YBRcsNnhUh)jy708g zxeVSTRZ}SZM!B_$ESxa*^g;{HFj&G;ZAqL&i8lAo;DG%!oIuCf!wd9*jpCk`F{0Ix zZ;hOHgWS`{hR8uDTXo{4@$Ly@G4Rmm`f&fi7%lQ^%nN4H+iQJUJIg5qI*w&rP|^>| zOGV-k>c#!i!khbeZ`n2Pxxb51D%6HW;Pc!=v|7W-EDwmqf?`;ZzdOqs$e`5U=9-^Zi9JL8pxjnFusIr|XzF$AN{LI1R?Mx-f2hhlQvSg)1u?9X z;NY!f2(*<@(dTuL+QnqE!0~v%If7_TfN&g#A z^wwsK=GoT0pJ+2NLDiv26H@D#srQZt&OPp^c8Re=Ja-4R&V_UJAzC59Q396b{sz$W ziyqs9wKn(cRn18U7El+B##wh6tP~;5Znplr7vEe z)iJ9wEhqJMe3){!VR%HIj&d~}9)njm)XRf6j>grI=sds~mkpPbm!okx+ZvFQ!8EOFi!D)ZWS zR_l-MI@gt@4)~L}p(;%2b8Eck#)kj;#?^3x_oWY*&j+Scad8ccA%AM%LnceL&g03e z_Psk2_0P9C@>D<{9lcy_fw))CBH;Bhf{@nff?^(6v~Y@E9mtrbJQgm4caOnh1K-`kawO~(TW^T8S9WRv ztKXEIY~OppUHu@~nB3&mjNMMBk0Occ!%|MGnT z*R>Y7sLaOGLiq_~b!z0tw*fv2^uARB{F0Ra`E^@k)`~B8n?PZ3^@KQ3VMm~M4x)43 z=k5nM%>LMZus4;>L`nuM0RnfAO0%Iy?)l3n^jTug#mU9nJXPh9k~JAEr79nn9@zD% z#fV+-RJ*#scYasCsfYTjP=TNNG9demdH%SC^7S~eim3?X^0ua z=#CBZwP;H`JQnEtgXdw#`o!^jYW6dx3NNNQkE^NmP2LUEs0H25P=vLL|2jk4xm>Ei z_5wz-&cb)hRnuVgCOP%C>FeE}<1felbF3O~oW=PHQil={eZKj|HOuD5CaD7)BYq7? zqDa!xgE{8ep6$}Lr)hBp)<6>FgK3(GhXCNTqo6M7qxC_s!SHS%Nwf&GOI`$Ro_ zdM&!jAR~bbMqH$YhKFFiQs6~cuL=0Zfsy-6|G=2F1i>oBcaP!eW1pr@@GD#?g6w`+ zGpN{F7A{?50kO_(f9KUqZ85yid2i)ZX&&#H7xj$0-G^V)KkG!M-&@Y)^8>bgsoezc zYnA1Ak&y&?w!gP)WD16GEnELcZ(b5vZNyivmKa#VG!cHLR?Z3m-9oO`6(295pVQzs2TGqt2k9T%T|P9l26l9?I>F(} zdNvR6LlgM`R@F556Ta3V^3WV!%~y4VAr~$vG-m!X+OTna!WH8X_ptujSlg6BD3`;Z zJI}AudNb||FpHc|{c(qg>c?NppcyPx!*-m#b8If*w>Dhc+;zKa+uF7JRJ*oqySuh+ z+qP}nwyn1(C+Gd+OTJ%n-e)qC%siRN+*z}(d)?QPPPS=3i|V B*?Q+ZVG!S+j34 z&xwV31DZrqmfQR$=m%S@X@#+ zAuQ_Vy{=qS8Bme#zYlGWqGz)m*Ctdo*A9w@CY{)z#^ z0IQZXe?Ake1nXnzLwdnq6Qso905X-876)!|g$*;mjrvaH#@=A&9>QBkP&;^zlvLq6v(i2NAD)8^OZ1gjEt23IPoOmu7ml^cGF7!E%2N zXuWLKthmSB^*+U7?s;&!o6f@-DU^{(Au;uR3Cn)h;P{sJ=Dk)> zCO6u+6F%LDNbAE6?Rq?O+hA|sV``1nz(mHTWp&Cxh(RYpFv=8)p!t>I#{5A=dDAu9 z6og(Trp0yyftq%o$N)Wl-p#_*ZZvnbbQQs|5UE_cj@f)b{1-pD*VMCdYP+?ez4U9B zAqd{`EKMInlxUTPP(+lUw#~|@6*tO6 zvA{dheNZ=RB@t&siKcVtMJDXHpWC3)5FSnFRjq54?HZW(a(f0=(@VbHVT`@wX`e`p z5=-DV`GdR#1vnlAUN4w?VoCdEPze64D-p93_XV6&`6uy{5EwnQQ&ST3I#gIL!S6VB zNn(d_q#4!4Hj&7TRs4Q?_H3-(+%P<|D<)A!rLG&dw_3=3Ab70yt%^&m6e=1Z446fx zpN8R6NDso2?ONB59BSb!^oRkF+ChcGK&Y-?(j=y(`U-a{YA5|SzA1_9w9v4I&jE>b zGmcxwTB0@thP(@#m>KDhvMQFIU}jmX+NGm}?BW@em$4vyC7xkn*HuZPiZhiAIwf~% z4EJ(Mo!Z!1|MvLf^3(^{6+T4L;~u|^GPlouXK?w5Uc5V+MWZ==x3f|vJBN6reCCHb zEl?db@t(qc2yT!#|6K|+{u=%_n6PiC`tU62)L~ntq|^u0D>;@N zuCn3zXs|-Kt-)iqQ)XH$TJ;5<(A+;IZ*|S#c-PH#VW)>ncGwj|K*zI8k5Y*cqD3)= zAFzMu_5AYuXA~JT63sQKR|>==(rwNYai>OmqgT36JzO&d`y0j&IS%C|Xm)wo9k<4g7>#W4nmg(V691_Kv09pq67!x(!A81WQ0;bei|bHrX3c zZj7TX8^y8V_N3KF>zu&*F&x@)+tjlH%=y>PUSut3-=(~RlAcMG-J}5Aoe1tzT?BuN*|TDRI-M#maB+hZkhY zCU0w(G7pi(MDEt{i{VK@Uy`wGc>m6j5*|eE9CpUXROVI*30=lSd!yUH=Wk6Nl*8J# z+Gad51Gg4fyghHwS}DnM3~{K}ujJPNFdQSG7$o~{lZXO6(IvWoE5iy*=4H68T~HEv zZSXWJ!mmlKi;>6B5hV9J5j_l-~|)&nkL!y7Ftt9tbp z#@Ii@cvb0WM~1}NC?~5bFKrjA;xnA|7%T0L?62=T`$y7>cx(zSZiTmE!MkL&K6+t5 z?nYtyB^zEHR8Kx~Wn*S$Hfy0@?_!|jvR*J(T>LEZ$gYAzg(vtTu^KOPKIMHsJk^A5 zF}iA2BvY!#BUE7)Z&uj*_oj#h&YwcY?4?Y|EfHa?HNw_SKkf2{+xB9b?H+FY+&12q zqecj8SG2ote&zS*sQKHH#IlvnVs_XVz%f+};bf>~!RLm^RMRaN#n6cYS{WSNwvw+L z71T9!KBf9^aC{^nbet?MlTp#TX0acGQQsYsV1=X8OwOh7YJKcpIWy7ux5itbMT<+_ z`$LkV{s3|5nJL19P0|sg+EXU3-Du=rmUzW(4Rp*aj(>I@9%vvFs>EUch8 zBCx;QOB~X$8zlc_tO+cD)hUMJcBZXU+M)JB4;Oz1M3scG`Hbr*WTN^(TDsmO(O!Bs zv{*0843E?XqwM2$IkL|gf@MKa>bcNE>8s+3h|O|k%J@(gNS;R@$Iq z{ykD32PF=*>SX-~D_05eZm)ATN8C;hxrzXi*C-JZcnZv+BgBCsC~Sls8CEyBYb6{y zgU%vioJn&c#Xb+PPF?NoDazX2^;`8w8tQ!)5(R65xR3qOKrY#hmb_^=tNh0B;nSxC zQ=gFJxG!f}nFJtund);|`vXl|4cS7(JOH3YcCssaFLUBs!TfKjscoP*ifOO#kpnV% z+e{?IEeF6t-{p4!IpmLSZm8Ip!{7#D`xbygH|KN=Yzuo;d0%;2bNWJ4KQinP*1)6y zWdJ=%bABUI2plvg9^mmw+E7`7Ey1IAgGh?R1e|BH-won^!o zqAcOiXL%fz`dp3kH`Ze%%J?8C1uVxs??Yglnr98B`>&|~)?0G5BVVzm5m?~zJ~2P)h^ZYFL?&+jss9MVe?U&-*m2jp)8 zkB3WWs`T-j=*?Tq=5BFAhRr#%!zJz`#6bO$i#wbI(jnL{SLSxT`sd)0)PHu92h6=n z6}q40^xR=1KLm#^Q}SaE1Xg`I>4d`;^z-Q|E4-FAF0>>Va0!cRkPpW(^?w;M#B!hY z-`r!oa5-~n(=Wrd#Z^7bDPj*{Q<9e>c&UGEa>GD-s1fBbMLk;^+lkkY$7~6S3X-DI zzYrk*`I3Yc5FGu=6GAP4=t#BARymTd8N4fx;@yRe0nairjAA5@gL+7UVVsiX5PsCw zXUMe@;-p$(iTE>&#dL1zELRk)?^r6@hVB>V#B}HTcT2CL)&a*^Cjkd>VEpS)`Lbl2 zj8S`B6LBrhbEs7>7}))ano#< zfF`v!L&45HzQ?$~{D;;l3G!Z^nq!SYs>637M!{ZOfSXcqKQ?|OJe0n*8`eBZs9R0X z1p57FCgQP@z>Hd?<2RO(TBpiat%6@3`nW2>bbO6L=n4jRt2>%M2tFZ(r-E4NHFqHr z`voahAjNL!9I7B+3ztcafnUq24Znjd($+4xCx|AHQ}#xF+ppwa9{Zti6G$h;8#v>* z(`+bkpcRw^%zMyr@W2IqrPfuY1E<5)S3-N*bHe%)@cM_eR#4&cYVjgPAQ;I=0YV+N}#O`Huz1hP0R>E^k{yYU{*+rheDT>)}nnB}^t$NmB4NEL}Ed z__BT!(2Or+=AVa zoR9{=a7A@RR2=~AMDZNDKhR&EJGx3{K^G$WX(tApML?gWeCu%)eP^@teE;;!!M3r8 zc4N;SUt?Rtudm|&YI*+e7&cgZCMhI)c;QKce0W#=Vvc>R{bR;dip$(MD;WGih$f1fZ)I%~O#|0_O^~%&G$*aQBKrOsFRqop=)R8e z$!Mf7?G>m0I=#rrfSx-qI8RGEKV8>&?ISIC8tRU(bzpG%&^CAG%vwLw#Lxf zjEdul(>L-ymNZI!dDiKy0MM|#@fI?p^Ws@!s`zyDF?ZbK zzXTsm@=dyxyeP>#Oo#p}?cP-q z;3-?bZM1(YGbu5a#RYVKi4|+2;JPBQa@xx)@Lrx>tz<_(##tzmpi-wm9;q9rr`cA@ zC;H_KZu;!slc1VC)nZ%Z?pN4vQplh(5WZsbm>#e)@mp1oF!==9M50Z}F^qke%`VmP z6{tSK7vW#*8v2AHD?v^-D|3y##cJ#!KO1pwyEs;mM^%SyMwSBr0I# zWSiNJp>ra*WyOl*$72AfaClNuj0j!dBziV7J_=o#EFY+L|7DYe>|*<=^+;2TJqGD{ zgt}x>x2*YgUD#B(6@^?)vyBT)`@)z}uEUeEB}GNcPue7Ww2Bf*PcLjC?u@@Zz4wgE zl-&lZ7x3w~?d3(Rb28>E8sX7l1dUy-GKl(JJN|g5yN-pWcY?~YVYKd;`2E{UALLQ) zd+?MPHfllgwECo{V|4n!)=$|2s;)VV=Moiz0iRMXuDY&4I^Yt&4x0rVM!q1-WefE2 z&5FSw4wVhwwC+emFT6Xt&S)q^1>2?@5qnVyji6C5 zK&l&L^!!pq#d!%Yh;8LMpI?l~UEp5C;oD3|#e3Ub2!BkoJm*n)`AMxs>=34VXOqZ~ z&plrmJCAuM7e+$(Qs++I3zR||;o>b>1Pb;uv6+>&yo*bO)yLkU@g^)Pd8M~V=Q^r$ z(3`7sr&I4)AOcvr$G%*Se;ScR1!mJO<~_Cd-S0N;xtoh=lai@zc+8uTItk-Ggn_5}dO-Bf{C1w&osx zM3s`V>kNBOXFG!2R$ZuR(IaNa*)MdP3eSo7c0qP%Xo)wewvOvv7^VpfJ8`RRn0Vys z>u9(=0n54*U$4ncJ>e0M`)x1rU6YenZMlz+xua?SPNGi|8NW95!cR0NHTXw_=x1A% zVg8(k`xg@1Z<9|+5*EiOvb&@1D^brN*9iV1y57MfAPyONPG>RGI+0MBi?whX3|f*@ zinR`m)TQ9aXpXq#)6fBqqkw$yz6=&+h5o~5uVF3V59(g1G>burmjul4(JRoQE8ZhL zU=oa!Kehw^I2|gXqJ^M*I%h`<+UYIS+*U!2o+FOFymy=W0ifL#vF4m0@k=xuUcsGr z9_{M8VF>{{(69Oxwf}?-B!Skc9xom6>z0GW5&2$Dy;nwY^JQVE-yeTTtf>tg>)#O3 z*_CR!ANpQpA!g3jizMg4^*y8ygk={)2d{4UmE;lP0e2DU;+^OhTj-uKrf^=%DGiI= z29?>zl|$#4Wi`lKEs+6NH$Z8m%7^mc3@P!+=ywWD@FVx8&j($$%ONJu{o!S`Ujkv* z7yYCBHozdXOzFbAqZR2c;n+(86wOvKk2ov46`XR|4?d&9Z8$5NgB~w4w()5}i;UX5 zl+H3+YTqrTDpc%-h@>Z&O2mxPbRldVptjUxzQI?egl!bOy|A`M)uPh2h`yT;YB7Kp zI@3?io{-F4hlfDxOvvC=LKyT@*VW~YlPUR5;@xd&7&tP^)6=M6Tt$gF>00$uIs9aR zTCdudQ!=max!2Pr=#R^HiQ=>JB2hLBs;Ekt^^#Q7eW)m7+YY| z&YiH*F~xbzas z0xUV0;Vtm-)r7f+8o(N<7@VoT74ty5;{6;3aFJgSmt0Jn8pcC13J6W0t1de4?p()F zXJ5qh>QhX7PxLD47xLva<>fO%op3zcf^z7gr=u%@gz%bTaxQAu49X=~W2vH*}dl{uL!RJ}GKuQcYL)C^l>M8d7+w#=lJ22|BX z6n(5vJfyMvm`;;_ABIG|1udw1S)WbXqdL_+*pq6EpnSt*hn#|NPG-hm&TNlKQSBTl zLiqWq5J4{Nmcj72JR(2}lsTNCI7TFMH)ReEVNNToS2K^~R<^M1-A_o9I#nr7Jsi_p zRkP$9F`j6c?kmMGY*((#g7xP+PhOtWbE{u@xqnG+zOa9pQ;?Rw9Lk+ga}u)`G4Qqe z!O5(&^l&f1l?TFm9Bz1;Mm6<5K34Y7k^QtASMz8-0GdbQ1&;St@o*MR`YNAs1E%kbw?vm2g#g1p zxqhR&a3qa*#O|TeSoM|<%doNXC#2*F({$6qM~k`HQd^gOi+d()n%v&f3IB@iXAH_I zrZHd;nLNohZ@TmgNMzbFNn3oAPotkP@0w$(vTG;w?94OtUn(?KCp&$MUH?LC&qA6H zv562a)F-cH@Q>e|dE9m0HQPvrj~O0r`gHOqFWQ7=TudsRr=j(x ze-)|-wWMTPvN4cT{y-RM$}YQ40fAv;j$b=!Ihd_MkqtxkR1En~^gguX#^X5iq$T}h zsr(R_*Pd{=v*UNt_H1KhQF66^koBYLcZQ8>^X6T~M~(BH`|~Uox@MU7>2fEsN;iK! z!`5?nouk3IECJn?_NDnVZMi_P!DgoerjAW?CqPNLc0Tc<3%Su`P>J&^troZH`Ot){DpVj@lBa{4G3zy;pcs#5BuUFD{f{ z0c|~C6^%X#DlX7Y3JC^eDNQ(I*Hgm9k}j5)6W=;UTI$ z#ORS6hAH```HF{6H(iVtNds5d9ICk%fr+&-V@t~u8g9HqLr;=WHU6nk@|pN$tmnE| zvgu~gS9=L+rD17I=Ps*ub3_S+(zo7@VvpruxNn@&ZbXP5)TWz{bmFmrTp2Ci5EpM} zBTd4v3~DYrZ`4gjYs)=mz2RHs&z+SNN`sI3UeJbYPo#_+r;LVKv3=etD5q_dJE)1~ zQ9t_N)w%lb7$K#4P;j{)3a3uKZ1F^MMgLgX*wyYTYyZZ@Zh7+i7dF?VLC`~VCHdz| zJryjQ?VUcPbAC6SL*1es&DyisjvIn*KJ9Qe zw@5ZmEYgLK{uz1qe-E;Jfiuh&6TaV+pB@}5mdnjUS7P3-U z>zeic?F|%VcPrpUvR(?gwp)0SuoRwZdv%yYG*1@)TFehO3Avc()*S+R8J8edFf& ziT9sEq96Pnkyu+gbnrik$l~7$r?W+M3zqK{Sk&phCmJZ|H09|n0jj@Y|9X3&ln?*o z9@dU#di4R6F&%`Q>LsA9J@p#VXlsFX+b=Y3_L6cf4AonexR|Aseb0*_wYubTRWZ6g zJOQ#_1Ja1_2dOtp(QlCo0s_D+jLllprUuXv<6ehwKyJ3y8TDrOZ1LlMkpz>3V+?IL1p+$)f@KC7>4!#jV* z;l!Gv3VYaP7n@K|3@~z56Q2c%sZIA(F7+Nm#%(;Y&x?mLqjQ&X(h-J&{CTyvrpXj^ zgknls#yC?@xPjZ0lyTkB6+Em(;;9((zHO@Bo)H|MHcC8TJJ zPa5LCvaw`0@^2rou{p1(cAN+nmcFmc>v zpv&CHBBfR3f^zC*er)jCi(t#K+Dq9mQ$^SFCu+d_>IY%LLD71u6vD%KZvmgZm(Q>pxT7UX{M^~eEj z8&}}AB#8SYd>7oC*M;J>ns9> zTf^bad8M!AqZc)oOnUZ~@hJfz-E0S%nY9dUP>zm~%q$cN=fgef9yU8-4e74mEgO^- zUPpTCFik`r0e`%=-*pK_cJF1O39WbfrDw$9y}pYEjW(?k-KU*d9kG`3b@J0A&?>0* zI;&0^Z`omwORRr(e+Lj1d|?6d7^PuwT5VUSiZ3Bi8D{a8At~HNn^C0o^YZ7?MsPT5 zPV2e0kW{f+$3KQK79OHOo^ajTP%nCRL|St=ldu`PR|X9}Lj0Hc4hGjJ+u6?7M<*FT zkA4GYq;!j92d=l9mKTvn&zj!IF{QV$p^5++u%QRS;K^Ekk)K|Cn!{_JFm#X&&^C69 z{+l2;2t3Y$XD^OUapQ7M6M+09KRbr}2$~u0FY$#7-^I&|Fz1?@eD2AWfDAw6MB$g# z#Aa5Oo&ld$7N0C+_$jELt8f6NI|K@G;qIKL`L13%qxQ1LYVolKouw$t~EOZtiAvwdY_ z2A#WS-03rp<~IAtpi5kvn#^Swx#%K8F)HqgGP^p1cYKr+j#^=4S!zmfHVm;nAnoq~eWu=m_@5QzKzD z2)8{6)6-m469$A74?e#sX5S{iOvUhzGe6mV;VBB|52x;D+z$c)-YfheO~ja9?IT2b z{_cIOl-j35ZJB+vJAd#A)gjGH&9I{iFU>+O2nxak%s-JCySOJ{_J80D^cz*NAUQ6F z3%+TGY}X)3LdkfxC?Uy_PgXIHy`)x4GL}UcMX#?jO4S7>R1dQuq$@eZrg^-Z7ggGZ zxLJJd?Xr1r$?3@}`o3-EGJqucwjYo!a^1?9WXMQs6N!;Ii6MO$KO;VXUC@1S}a$4xCa(GjDUwe~aRSU( zN!c5tTs)J*=Dmj$9@*^5fmY9Qerbu(o2AqrmdNx(6rNc{8Q114J*qkVUScp*by<43 zj!`|0g3eC{_{(p+fVDv^Da?B2-mCx2{`(H!OOQt3h^Cz6Qa1VFd>L^%S1vw33KNmRMfja< z#~SgLKUc1yMLql#T;HeP7hVsENYN%qv&i9SQTmTK+-1D95K@_a!en8&y=BIzZoOgr zx!5Dsly8j9>!z6ETZ{EM7*phNh`hc@pBi`W;f;<@VOw*z$c*Y-)lf4>KF0a}{Xm72 zx;wEJBf)G5$}{6)K>mWZ&0thSt%+TeG5z=>-k?J5aE>6sP;1kP-hT0`Fxfhr%l6_T z_OD7H8Hc!o>B6Ra%o}dU)kxZH$@et_dvXtPuh?7fWcG4S#!ZJfUI$_VDhch_$mgZ$sHlUKegM6PFB z8AsuvqgLbU<4gCcwY7--G^Wh{Qze}@?A5dx+gI6k<6~H^4huyfgY5~$vXh!PZc7hM z_sMeB4nGf*M{l{5+>(FoD_zRx{-M40)F&6*`O1AtHlsMRrFO}N!)kW>@pSU(`O`mX zZM{*~h3|@sILVkO9eilu=%nns4O+`yQ=(RX)_<*iH9bNzrLV!!v&G}XYKqnVN}YF7 zNHOe`GCFdwPiZQB0b-|N1Uv*4yfxcpdc5;kOCHFQ2tM77eCpoahqSI4#mS2Kbmx%~ z^sWcgnWbt2U0G^TmlGcOhh6Zu5&X^5I_rl>uX?>7ls5Ub6yK9PI&XhAFQWK7AiP5d zVG&+=Y~6QMx#_n&QT zXxPC<9fI%`yxW%eTo<>xCy*~N;Ltm8=8kl{s|Q~q|K_@1HE{lIj#Am$f}!iKeqiBI z&l+-(huFwbd`G(JdGk%Tm`t7?gbl={)#3LkQp#-@rarnw%|B$DE*;FS5o z*XCxgdckg)Jg&as-p#fZ>64ZU_c6-UaC%clUqVc^B@c^ zWNE!1D{*q?XtK+%t&0AQup0C()8mZ7{FA!=j(K1)=pRP`ZMEIoN(E>4@CVp zL41fc31PW=P`;F^u&P>blO^K!_$pTrD>$r%7)Ot&n0X8>o^IooHPuaQCn_2`MD#c{ zG_H(qciGF?PtX!!;#|3xxwB$gspi#Rd2Z+-R#yR6a=qlUL+#8VQ`Yt+XQ=6aaNb>> z3DdZ<9aE=uy_}n!UPA6OypmU0>jZIi`6PugE^W=5F(Xnnc!{ynx?4a!brDz#^Q-~@ z03w4AruFZJiFZ6wXCi{CPuD3s9PExGU6~6OA+!Utm(?SlcjC?3sL3HoU(Dk++}_zL znZBxe6ooahvhGDiRwYXek z>;#t1A=k&pBNJGKE-SQ(??hBN%Ub%;1#4*8ALEwRK(y0w=Etz17(Re!p_1OZ zA$Yd6pJnm|%n(wl@px@4m8@i^3V*2&@c)RP;u01k^XJS4^goH8<9{uF2P1oDGXtal z`UDc>!}r;chO;XHjI0eN1ls9y&4F_E%T_L{0cjU_UaC(57LitqbqA994Mhj-n4_|3 z_J?dp)Xa0HU-#*DWLt0@LOc2T(oylXU|3RfLsO`!V_aEvBqE!?h@4bDuObkloC|8i zQM0g1&q9k5YDq3h9={qbCsdup>xt9?hhJLTIUG8tr#$G zm@#6NQab3nzCh!um9~3o?b;-%lGv~|5GMlS6VujcDql*h(~!Q1vx^{WUzZBO2XO42 z^()G9MAxyohj1BagFvhF$|d@?Of&R`J28Ht(jM1cdM686(AcK5kgmE8(z*sW!6oD8 zVL&cu2)nY15-r;vjODinx^$ntRc%R#U5Dv@43(bdLu-cbysp~Q=M`&GXqHcaOh-%R zk#XEp{WLfOA+t~~MLIL0%?ZT;=({3;-$-~jnS-nJ_%e4iMB+Wlr;?58WwTgR3TIHe465IyUGY5#_3Xbi-mO1U3^RKo5i zV>e~JiDg{xD?e@eI;g#?kf!H^1|kgmCwCB5v#+_tS~e2K;;GaqRsZB-EDwp`Hp0TnE*)J61!&QF$_vEF^QEjw_%<92rzDz39+^xUk9Vl!U{10XM^Xx!_m3yZ3?RuJ!r95ZIMcf1UMn zh6?_lbb$B&RR?q&jT{_*6k$VU+h)B9)hFpI>>b6F5IhXmGrSY(M(DiJY+{(ue5+p~9UsS@E3TxiC+0@l0di=%rWpEWaND`1_5$B6;%4Ma+fup6wNk>}g z=`pdt*uhAt(<0#7mP)(}0qH}tWLL`x6-@4Of4LJB5A>DH!i^CN1?UKYtahULtGRyW;S*mFA|9Qi4X`3AeLiK_UsH+Yo1$?Z66dLlJZkVydQX?iUJHj?`Vhj1Xl6cT1qf=S z=2Ct=Mo|aZ+Mi%hiJ$CIKHU?(nFA06KO|WkHX5A^z%g#V@9!r7T}ye?gdY-_(N3r- zQcX5md_y>Us4{&+Yrv2oP=d);kz=(ik8W%NaM(y@c8=&ubU`%$B@`eNIqU-jlPK!m z$dq5a$>?Ne5H!LQ181GOSPXr7;k~}>cj2X~5Y!iQ95B_k_;GD;Z{}j^oOFpYGbtbN zw|ntpIq(Tm)~=^BH7vYUM-{YE4Nu)aMLMpf%gbALD_hD6oGNUK1|!IDEU#oYZB^)= z$=J7}psi<`65myn2*#51-np?iB3vvx+rim zm~G0%(t=Iz_u~FFOjJ}M)1)=hAgd29Ybt-V37!zII&fVj-zNi{H~0pQtuVJ(+cA(g z;(I%sUn>|DTHX4zLqN2>-k`obTXr$lc>nIgX{#o=VecTR;EExf4-v!y!#C~@Sx9zL z#E#{r-lOntCx5!f^))X2#kIP!+qO!IM_n-0pL)0e2s3EPb}8 z1Ptw6t9mCDdPMNG4?%tgxW2TGeaExeA^LS9A_N2|8o2Q>+E3zn_t0t9^5T3kAL!Gk z9JYQ--bUA>JG=QmYVQ&I>T>5TSXiH+o+lzfR0a}RiVZx9H;<)O0Xg5gkA?JDId=Q3 znB<+nwu-VIv95uv;Mny?`j!?@$NR!A82d#ixs6+>IiaZlc?56Jem*>@R_|Kz?i%m( zZsX(F>~;Ulvj1&EV$WprLxTYU;UWAd%whgtVUB^NnUVE>Ax(ku57Ka;csG58l@egL z+LGd4(FXJUlDRS`G4qrS6RA~2V_~gpNfM{!tW%-5>!uGX_#i&l_21-UIkJ73yh*nS zG7sDcVHLCyCk-`MH-MfqWJzkW89i7EwPV7@VV8}Yb*(HpQ%+@(=@Q*z=KOxPv*K&h zkGJ{G)cB|r60>o_l;QP`HxMuWnJT{Xmh>q~nCKFQJ6Xt7_4-WOR~U zY{B=#N}PTg=Bg*&njw;_C)#|o%Es{o0~!TobZZ+=e7Wd?W?Wjnb6WyteU0$9c#_}C2{Y+aZ-4TvEM&7U z|2BgNFd+4#U584D5|N52pE~VLZ|NgeF==Vpszx2jIV;N};#}xeHkJ6nLHhqCD3QS1 zI}FX{9vkn^3g`EmQ#FkkWR6fa9@aA_hVfw}=xitg2Ce|vNg(4z zUVr|cUO^d$y#4I&^jkx<5j~)Jg?v=JAq;)+`!{F)Gyo9Z=`Q$em>s%iI_}WIZcnVt z3et`MlVTyL0TVh}kGX@nF^EU6mjr3Sur_g~l8}&)nFKk#fcMwzEcE3y`7eOO??K2m z6-3?QA|O#YhXov?dBuZTQ@b~pwmz^1AvwBU<6<8EDV_wYbnd;g9i-A+^p{Ac#b1vQ z!-otK|68w+5o>MUklW@*?i#;YsL1Zp0<8aO! zx^Vv%a8HN862DXyXUmR{18{5Xa!jcEi~vxzjh25m{`FjlEuvwn@lZbn(gPH8TvQnF z37DPh@Jtm+Ek!nW(QaH0N9=eb#;+n)9WNudiuP>~0r%JSgCFn9hnuc<*}d1F@3uG7 z@*S@Q6y*L4ai9DJGg}rB^<-g|zc_^jl~rxi-LiQW>W zG(N8M}+Ig{RJhtDz?i8cumZMOv=wLM!?AiLuUBw0i_rGS{5u>C{e!M@M1d@rFYc zO?ahYBG9MiV+BLTFl=|lJ|WtC4Le=#k|W!@2ldV+u*QqaFOiXz$E4a~M?`*JE`;sLl3wau1>?2}?sjAIRP?5l5)Hs*(2Y9vVAd z$F2}hHm~S7n8L7j&31^1cCL}F2l5Uar_eKUA1IDS0Zlypyw9se?rgy-Kyok z(erR<0m zcN4Y}gn=cSb4kwl8Mj7W=TC05@pZ0KMStusg^@x?7KdUPN@iCMO6O?}u;9&=OmWd$ zmL^)!x*G>)xri4Qe7I_4ODN(0VQ0dyk6-1hl2=l43|Q~ZWSkWp-Y@NS(+;ndF@)fe*yW33t`QM7Xn zvlaz)9qDP~Qk%3YOALenHfSh?94GAAClUT*-v+Nie(hum17-hDPv&IAEH%&c1bvd|*MeA7IBjEKpqCV60-4kh&*6~nuX0#yA2<#(az67c4@6-5n5M0?cY2^s?ZlmpRE5d zd(5`^(6APnq45qBWYdHVOrx&C&5>|=Kin%~Sy%u}+&M z27Vt5=4b+|5F4?Vbmu%cI|4Y-!{4!_TfIblX~eb5Eg(O2KQ2UwqlKQyToXPwTpWQcwXv5RP30w>P*ZyJkp}6(70$Yr7%I4VzVK_M!WsI%| zXzL)H1PO1SgvQ88CNL+oV8F`pCiz;Sx(^ckv>_zi!}{umuqyg9n?NTOFmbDyK7-h^ zF+rbP*4b>)E>{hlKI)`D{=s9kv{r{%|DLc3?)sqS#Ax3PBDIB2=K{w7>B4NR%hK|h zJO-)odTCMep<9zT%O>KwZrx~$t;hVdxf;CNI|ISWfCVeN{t$6>>|RzFvOEe67|SAf z{2VeOBZ0g(P>b$@VLvxk_|4m||CUGz=?Mf)@~q3ZTZajy1J@ZTz)1XnQ0Q{FgxVNL ztEYD^z~!tHLC(TkO5+A>zUY9@M8<9{0zOCBJ<`9$ec#*KU|GHIXAWVyNnMeM{tInemNO(#G!1=dYVK@+5QRFY6jc14@b#cJH(-3tP-uRM_3S z=be-owg-|A>y{E#F9oE9mH{}4ri>YlmcSD+qsD5i+c~na!?MBSHJe*YO=~#-pzoo>* z?t>F4L!GWvB<=!fJp&U8X4S$TEc3t3alFGDtmx$TiQ={lm6fkx-=2?Po~-h6>><5ej-_+{)3KlzJK2t4=Dq7|($ewHV5WEYtIn~~GL?Fz3Jq2%5sTX=cDL2};dTxM=j3>wt=>uq0oh0+ z`Rx#J2U}21bek@hXxL)f$2Be_r^x~QrH)tn3_FUPysSzI9_V~7 zXT#`N*Y~j~&;^p^>-XW;Vf>ueAOsPENE4mEnPXV>SzC;2AnI_Z9=PEFo%KNM8YddIB?#PN;_oSQf$ z_%B7D1y@S!^F6S(3F1vH<*+~-y;nlcSrp>B>#Y2W4Tv@J0*wfMO2o|`|CZ~au%bSP zs^?>Nor65b8oMQT&fVRS!$ZxwvzuinG0E3pIdj=vm)8Lr=H)3Aj3?BFYi@cnT>>bQ(tbgQ0 zB`!TBD-HN7J~d5EM|rddG7p2TnbAx4e`tHh;7Z$VZM0*f({abPZQHhO+ji2iopfy5 z?%1|%=ghU&e%Gn5zFKdceQNC=v*vvM&ARXLTtnBm#%4BosXK9+d0MG3w47df?tXSP zXfmuMHO1t_l%RrPd9G1~sad{h?m*Ef*uU!MO|#x~;(-672KfB@lC#kN>>oS0+d0`f zJDFS28vfT$-GBW4hly3F`r59sAbza9BC8@`>@)w;u+pw=z=&fTk^|icct@v0?(GpH z4XzP_CbAy3zS{^TBoA-eV-kOoHINu?cfH!cE)6kOR3R|3SV3oJ?1&)HVO78gE9jl( zmccmH?;dk=45V<&O&#ekGU+{i{8PSV7EX}Bj%K{7XT2mLZoGloh#@kgr{>_u*e=7M z{+l?xh>5F+*RyDS?u9AqD-)?}r+~_Qh0NTD#JWLYtgf#x5#@(5fpXev^HP1^2f9cB znS>&nvBciW%MKJxUfvcadS-Uil&)aGb0$U*IZd8iUC3DxKQ;>Eui&*^a+=s z$nOKZVu9F2Dm{?`U5*;nCoDl z@sY-X*Sp_BOpikCJRWzh`T?V;QlA(+)SwseBbNPA+&>aAP{a+y8?>IZ2I0e}l5%Ha z&}60V(+}3mv95V&q%%EGO5!P7rCS#&1NHg03MgCOJqvXg^ zYqk$2*mnozMD6{)l6g19mS0lcG<1I2^Frrl>1U}K1AD4&?52o6(&v(OhVzHlP!k39 z;M3o9?9iL((IYQ{o6$?=VyVop)zIndn+3~Y%=Ckl$fhp?i7g90b~U?tLKkI*N%~21 zO1}QfVT`gziIx=@s3tIthFy%~Cx$=3N2ujxD}PujG}DH;(5byWQGXaYeiOEihf+tq zsbO6m)m+qOMOEPMci@2F)2BYFzy!w=$x0^z89W;$TqeY{*PllzO3US2q;`=n{8sU^ zzCjvDirhr=Yln{08=Mvg@_Bz2I$tEPf|l;~#vHDH^Q_zqZzKffhiTPX5aITjD&qAU zID2fc__`iEjE&9t%47dpaur;hIkclvhZ=BsS&R~=o*#LT6h&}aICvoyX+!lUQ0Noh zCXnyTtibM|pm{{fdxY9##l@F@x1`@d=a~m-Pfa(_E=A^N*9`4GvM}cpGabLF7@Y}R z=T>kUiG-NdhTafN%+FA1)=17~`y&&o;GPMHIT3q#8%}92$0m{TKWWAAh*YT|kMO<6 zuMCPwCSZNcbI)hAxx{uL7(eWy^^kwG@fFei=C_J2=*=wkgby!KosGpJMco*SmsHNG z2adIhf-#4?ly$hb8PB7~0v`tBSIemYii$ot%3b#r)8xTLTjX^_uAW+!S0hR>$Ws%1 z8c6$CKpiQ*%rchx`Z-}fSCH7{3(G5*rL69Ot|@MQrKk_WEo9@m?pDx0`hvR`yLkz0 z8{O`1Hi4Xeq{Fd(X{!U$Ur?ggQCA6_CZHg^9nj%X{HSkwx&H*=o3@~R{dxUciK^x* zR@>yEX<2%(#ZJo|)L4bomzm7qti8L9ov^puboYk9zYo^kaGls4PTF(tnEz!J!Qyq| z0zsnPcNWc-6O;$Nq91L&l6NA-(&#NY%k2{@FPvU(2eb{F2WFDIEMJVi#4vVX!)(_~( z*ff2i$Jm`C9|rTJ-%Gle`Ti*bm_#Z0CK6jUWwKP)k5Z?mm2+ia=V?#n3)#@TV=Q9Y z{t3^w$BTPaU*`LtXn*;qpT>U-y#STC5#xk=TVF1#G3!$&j7ZX2A1aj4HoRXm zx32C_=6?SuYb)3IHdDYQ6~JWQ;jpq2%AbhGdZ2K?ur9~1<$fYuW~=3D_)wP8IE`}- ztcw6U+>D98-0vf2CdQY^)}>6IB8f6Cey&u0*4w4;PlSN<8z>_kW!|9Q8Y=^C?uPF` zFdp%Fm`I;BDu8G@5I6+|G#TPlWxKRX8G)c&e!`;E{iS7uZ~cJ?vIG58ZVz2ZyvR$F z=FA@#t%^g~0v*^8)?xp)U6jIx2hkOxqm87^L*?S&Zs@c_H@|a3V6?MU-_oFw{cO~O z*2jHr`Qy__1LQN79u zF`&{PQDA5)l=ppa^1)qC{zY16MEd`sCpIOnu z8uM&~L|Y&)roBe;zMMZ>1I{YP?vbrQeAg`ev0`cDCV}UEX@%xuV)exxvzkp=K}DgS z^6lB9`%;?RGKIa)7UJwXr9t_o$3A?IL!D48Mx=P)1SU=X^f~D4ikjgk&=t;P{6bmw zaZFc2ANwWKajgKjS4f{XVwg#B?FHn?EQ)*H<uJ1N?c!OPQvG<#S8rb2@fRr|S0!xd( z>+d#9&@cndMYGQwN&YJEq~MWkhl%XJPR_J3%J;xLtTJ$XsS-DL>t`BZ<18{%DPu76 z2%GuyT!LFWfI>u$QC%8Xaf-@%SqY-2iC%T&#gyo7_*?JLXZb07h`w$re41lJb&R-D zn$E{Q%6~@n2TBN?_1w?d8M?I9ClhKL>$({%5dGMj(YYv#K^L(I$?Q57R7+y(=)?58 zMg4A!b^*hj@57pI?gfrA7{LwPP}Ki=ZDg$^)i%d*lmL{46_Ke^Wif{~is5rPb4ZZY_pQBo)-3-W`V`$cRrp&B67W^+-pU;g-8VBPN z5zNV?HC324A+xIH@5o5mHDZ=jL5dmkn_yJXKx~KqgjKZ*$a4B!#7;&)ihyutxv&Rf zkKLitTK2|hP9!^os)9x;eQOhPWlqMeSZZ72atSZ(OhhRA-rUsQd39&AJQp>iB_T=$ z-Ik~KZRW{-%D{46ZYo7{{!i;5PzB2j-N<|CP^gtPTq3C0yqe1qR{0UObNlk6yD8K6 zr@*SZ)4RF%w5M%bxPI#mLl9?{6~p=Oz%1YuY>xX=?;UR>6VxxA~^uUK&%4UNOM zlfTYj66XN0uB$!CtU|^C4dxVufloj{Fb^n1STbb(Wo>O2ES|5n%_z z*Bnk9LKnb>4fDyuY~l?btXm7+{VxTm@t2p#mtQn!lJ>bkbrA~*{)h`DhQKJ8#L?GR zttUwmQ^@kYgZZ=idgqJOR(kBFW*ZvpW8r>p!Rzj%Ou>Q9tk?tM-vBWyj6d(EA2~~* z*s;G&6GL1&fh>V)Y1McHxjsPBa`T_;0p54t!M+vcKL?4#Rh>7d4eR}bfQGdI8G#1R z_j#vFEz8HBkKR(#BC3a?=!R?YLHFzEqeHan=|lV_*5%qhpvnKTPabW5r2F#w){V_L z!d8&VD@j9>L796D5BSSEyuy^8^6(qWx}K~NiCqFshwW!sFYch7AP{-JP~I~>Twq}; z8EwnpN-p^ddX#{5^;SxxzR0n!>?`eYz=b1LN5p%iG1;})eflT1g8h5JDLZ)K2iQ$u z;WVy}!FPBSp8en*;qj~Xor4;$5d_ zq`vKW_ju=W=D`sl4Q%yEex&TaV~xFJJQkn=94_DQ`@~6%qr8psD_KP6*E1^fnhXiilnLk zVOn|tJ)gW1D(+L8*rc%<)*Skfq>*E!^FU9&3u?x*iAWmvts2O{U z;&OYLJy6$AlP2l03>>%gaN)``!|J_r1V?oWPQy~ChjJFaKoRKcW3sb{NB7J=Ze{(h zd?kXrsk1mf190BCU#P%f^L@>$(0fAy;Gi-lzx?%Zkuf^ljK7F*J4ZTwGP~RCwj|$h z>An&Tc|wcw_d1Ojhvnd8x&E|uS9>8$X>M_U&E3HmXf?23w+NKRzMQh-qZ;=??7cAC z<*{iA|8{U4?eC5+)V0XZkJ^b)?OOX*Rb8cBdfa-D@LFhV%-+_w6Hn z9DjQ}0sCx2huuvv)h*JoTyVY5kl=?6_tRL1q;u~pN@;5eP}(UQ zUhe4ZoG;|{W%wLWD-pR7jnfg`)qn!h?}HAZ*pQ z(ls{%^j^)K+;#ueg>6XwW3$GFI6SqhDCJCc-j@XLc}D<22(Qp@zPAh@w^KogtFl{@Z4v7MJd?V?!yF$Bp69mQCLHAP(=irQ_Pmua+%0i zSWl=S(-NUPaDDekqKV6tJ@xU{I*w)0EI!+sU92QgA41!h+802gH4cd&VoqVlc$Y5# z(6DI?1t=xy1~Qk3y|XrZQ3k-|jo-}2DAU+^r1_v)?fi4_tRJV`U0LnQ_-_B^4*JNVRpI3So z(2y)f1^ucalh#kB7VZC#DB(+m8>Yj2HPXGr1?Yq+(_xZatBIYRWf1PSsbGj5(25DS z&{?yi27k@7Gqthse6KE><&LgLd%$Ar4M{KPE*i>jB9as-1H0=owCX`^qhU;0G_EJJ zAT0GKW~qs@RN`AD4s@T3KUEv}?B#A(O+oOa@kMj6)+>U`2GTkvO(~w|x*10MBslt?uZ-Wp@MNV#EVU^{Dw@@tyy%jJ z9W)}*a=4u9Ab8@;;rdSg?4xnw$$U5l!Yq(p8f6g7W1JXTcB8zn)?+;!4f+{5x1tv% zZ5l3}QY&`&x!F!BSLgISpN>!>nFlm78)FT9D~GxVu>JF)7j4V%*O?&hx5rcc8ONVB zV6NRFhDITQqa%$Xq16}2#upKOy+)vy^H7oN&(G}E^-buU`p;7ZCpbkXp!f|=+_j6m zP!OuqpF0s9WS_v~L6;#VG$j)|beHXvVSC;k5X01lY5nOb%#Yu`l9MYevztb<7RTG# zz3DK#zc@N0+q(gqBYd|wvQ2Id1K}@|ZIAn@7226m(N)wC&rA}&+so@6 z7B4m9efu?rqzX*i-!SUHcY+wqiy*B1b3cgj2{kpZ(Iy(w)}eo22c9MXjNCxF~f(hSqtf=fOG0#mW@~8y&kn zlblv>{h|>kv$UnTxASZSur~0Z()xUHK!*HnNT0;?4yJ zZ1^W>N3$5|b(0%QA)^}|8e^LM)dD6Bh8@VK9n{~MJ0D`DPO(*zJket0MQfJtt{>C?_ zM0&=FcFxvVm<^HND$v?d46vrIl&-CW<%wRYrJe5p^m25XkO1}!{b`HjHPhiFsOr(%?Z1U{TbpG*Dlvx|4iNQy+ysu^> z7&_+Gb-mjR$D*YP9+&~>rOD{K8TnZT8tD^Cq6XOL$TcAf$&gN0>9|@6ygV}-{Va~P zI6X!4V#gp54N(A5@ji3VPp?y)8JB6KQK9dt$rC+`cz`nM&EV?r?J580T@>PQ*!-)6 zik5M6riUsjMXDb?YDa>H;3T$K;XL3nlN%wAqIqU*)JTmD@Atk@9RtgBNcOqjXS1gDM<&1hJinBhT~Qy92EX6FMA7Lf~?OP#O@i$ zyjA)B5}X1U)BL5xGvB)O!1*C^y5zH`S+I3G&;Gn>V=C-%sR-_kt{8C?;|EuhFS#?X zznL!MdKcnHZ>(H!If-=Eiz93pnLDu2W4nxpPhq-Bu7&!4;W$^d(R{KLVj66ha_i8a zI(vNV3{bE5STJEtTZ<8)@vwN=p1tXmfyO=wLCM&d=`lIM=$L8|<#t>DoPn@OIZ8tW zX$r|b_R*UaUQlQm_PJ%?K^ z*mEc)`JnlzT{d+Hb|;2xT4q1pvdz6_W%ozVk=%S)uk^8xD0LyTa+;+2apyOQffk2Y z9tNHqhrDPs2^Qy)@op{WtFj{b~HevFMDwd z-P(DuwVUq#fZJ_ys{CXEhg*BrYs7XRP|S?gB3S$;PCO&Sic@;%N=SpD#Op~53E#UkAL=hhiQ7frTiWBO@XGhCmKoF zm=DU>2J)DYVGSND(1#oI$<$=X$BL7axS#;P%_F5GIBwk2H*e_(8`1LTo3L{(`tRL{ z8~zWn+oye*#(gr4Y>Y=zVV0=X*Va|dGS-)Tk>@YpzhGsMUFK{W)y4?lFI{~>(Umfe z2Ds&9!MT%);UB9*w4M&dbu9Yx^A3NS>;O9m$?GIu;>NS;xkj3Aw2v)*vHpdf#?tkX zg8=LV`rGE^|E~z|uK>KYkY{@mfZ&Vfxn^%a6Kfi@f)Pg^K44y zbOazR%?qiLT^D9X20_0(R~9syG9evGdWU8wJ6P`yFXkCE$C2<=iwx9Dv6v}0q4HQ7 z@k7Kw@%82Phw>2l)hK*yRLxCT>XF<{)&87M8Jp8&cyX`!q#BE`t_NL3RE{E-^`#k+ zG+JP6rI_GaGFVyZkQv}dsF=zTY+qQa3b_*r z84$d_jq*GNRVFGfg!rDUyf)0mRpk;jCvj>q(r#Iq@14fSYZV!MjZwcFucwtR0a&LKTY^9hB= z=l2pzOZXru=w>&YRZWJBS|8B}sjTn$Y)xKeYzsyV?%HdESba6+A9@3Z2{7+#(&Qd@ z0-asl*k?K|SD(k$m1UcxU$v@o>A#@UR-pKZAUs@G34Y`p`?EnH}dJZJWHM8HPf z)Z41Gf2u-JMGyE5+SG&{-zIczXKov7T*Sbo)nIMmn&_NvZ0B>kVI{hL{jWS_yLLfR z2|!4<1OK}SK<L0@z6^VZ=_UHgP%c3GI zJ8@Aof~V%PU~_04Vlr{j5rYazU04PEYzuy3$6Nk#^xk|3R=Qvf*Xz{q6r*CnW*+{s znju)$MjRR?M3I%UBVuZ~l}SaSbnU#>LCAiO!QK6n-ZQ?9%gbAA2Yg3ki~}-MlUa}n z>*#DPJXNL>W1}3!)XvQugj)q!UowcEHfOm!l_%S9m_&W-Z{&nSa%`NpNZ(q5W8ZE) zmz@LZm+WX5-6hIYRF+=pV-+hmjicr3AUo-FE9&l?oMrnK)R4tj74)nLQ>l+6Uuy4~ z?}bFiJmA-;(+JZSm5I44-3iV6%>2R%wQ8lDTI4;>wDHSOa@XXP|(MXuo z*LiiogwD1L@}<^$%x^L>cxi7vCan7 zrFr=w1oDP^*CK;ZpD4y-o+vnrF+*}?sTbsB;1UskAHfV+FWh+$wGK9xzW`rO`lrw$ zRj_(LJvlFqo5oY#d6gZY>CVZ(Z5U_aJQ0JnO>%1YxHiDUi26BLVppFnG^gCO=|n5t zuoD+&#aHD5KTLw!yo1lWF##0{`FA?QM>H%NTwL@0)XRDQeu~r<&T*0<1_Ub(8s&#b z#v&tA08%@^cQK*WtFb9zntIn9h)EOk-Q;)D{4WU@vqJsfZcsT*0;9FT$ZL_z!PKD^ zVZ&p8gcL*s;e9EXn8NkVu#4ifidVlN{(@XhPZ&#DfU%DMt+D>2*Z=>$zw>_?>tDnX zT76SvT>ve?+~mJ)_a89q1t5{E(ZTXf?W!Vj3<_G>E?4>?3|(?t3DOE8M2JudAdlvj zzz)Xw;u;lcr`s^=^*4BOX{!KAbkT7! zUR!fQ#~M{c;+-F^lx%)E@?rDQ;YE{)r%)Aypo3*&_rQCsZx->25SvLSPSFcu7>kJ` zPr*wyVM_IO@~>HR=@2W!O0cJY$7*Jj!jpyR+%erT0P_t@$I@^2LYC8YF?!mvlsJQ) zUosIHu$BOtzvqH~zb;GqGzOBflzqBC)38*uI*cOeBDXkn;tQm-*E~!^KSa9V<;$RW zukP;ubD&kl%_=tWAhW&zwNLEU+kUfdf7!i&Tb|#|3em>z{hL!RWx~f{p3PE&8|s5- z)K+CVRyKC&#`lQBAoArIGWXr9iDe@YDwqRHbiOaxiTaB9sN*N$qO%-nVcbbp%}S8g z-get(iZ<2~2z+j8p&!0zh`l1|8L#yjWBO8q_`FK(dZQO|K+*-Oy1Rdi}|zW@Olx4oiu$WNC1Ax*%Q}tL^5*e7+hK zloTE&vm<0;nR6vvogY$+p1g9rqC|^Qi&K5e^jQq(x`h(k314y3+-H5r^NuM&(zug^y|3CJq5l&C;#)^z8%!ySVkm|;2!Jske+t<_dARbCttyCg!~3y z&6@HQ7;kT)jYUYP;md%Oq>2~Gtz z3yvOwUv!YrCrrQ`UuOvVdDSmpCorZ_gF*)y|Ma=uU!bY8eh2M8zAIxsBs5214G6Zh zj=e1xc6Zk5ad++7&8`MPvht|rMMf-oqXEm-G$^gjlv!9zs3Iu!uiBh-N=)SQHYh1k z@7Ar)lJ8*_NLBzmXj?5hD&77+?-!)s-lSbkx>|){e zf8HF8=jOQ6iiuCtLRt3JER^vX+U=!1N}?u7^jm{xo2pguIjZA>?%=2+w=%Hs7Bq!4 ztS7tVBZD=g6Q$~!%R{+Gdql3L6-EimBa9AvM`CP`1Rg+BG<5F3RdX_Rn=QCi{h)2Q zo4cwOCbqwmXELL*U4@l9qpuIA6T1|5k*-S={zaDcph_8+?{Xj#9IrA0ua1nZHH|11 zL1e#Vi`30PyS8^ow1Ago!|hL$6>b{VY#r&4AVs{=FiP%7{=~(p|80XtwE8QC<5viY z!sV#qbyAu2h|SbrU_(~;aDglu^A{XMah zhkSjf7UhkdxJb@i5|EY~aZihno?=%Wh%EJy-}E8@2#aZd_VNt-( zLYIS-jQ8EEn59v+KXb-(@Zh9NGF+y|f3Do;GWhiif11OMTz`v?dR-tC71|+Hj! z9{$rZ5lXb%v>YXq`$zd#PTW^WJJC>a;~VLfJtm)UrH=m<7akhI$&w+uoh64I^VUa0 zvsi&)r|aBq(U&VhPR1rks}xy2=kqT6+As*`rbt^=;Q{kSsu=i>?vvO#7%pT>69t%Q zHhDIFLN|2gE$rXNkRc*-3obrYJfGuySRX-CH+m_()TJD*xFqaE`y-tcg6lWbCr_UR z&QgRWEvkkPkFo%)_$RiU(KLe30r-offa3hW#}>W+MQr)YodDK<+OF~;`3(6EX5z}Y z>|=iQ=n)*?6PpKD5zFIqE6WF=GSZi)Hn!`K=8}EAnJv!C(W4=wv^~Lc zmbN^mN2xb35IyO@C{Qa7U^(b^wST3_;bFr4D88T7H6{%2QS6nE=-T(uKR|D zAp-ond?^k_M%@Z1T!=sZVJ`tH`hw>N372KG_U|XAOGcpIrajlAYU((3^?2TKO>bMP zg2;kNfwh);{%5Hg1(|8yRWu<1^?cp>l#YCpitw_GMsWpkjFl!}M4?d8{tS5;tC%OZ zp8QBqPIv{IT#QZQ`WoA*(^df>rlx*dFt1`rMgpFtWw`c4z*8toAyCoRAhJh<6N3=x z+mlG^bD$V$hDn7`&90=Hi8Yc~>M!w~S1G`b>H@<4Fj2GQHa)Ci4l&S0z--)szL$bN zJ~Nmjt)y`CoNpn2pr%ZT=qf=w^MrnhhpHnR^?_#hp^X!U3_znI0RG+P-lDfeGSpC{v-am!c9-euL;xAfkr^{ZZ?7W4BvQDPQ@Pqa8i%6N!fIYY?m=;E zjm~ZSWwCXwV6QEbTf@XG6?NoHt}{0*%NC)wmwdJ;K16sq=S;I zkDAneg%RIJ*^@A@Lim^#sv$%k{A&`CFqFGD50;LuHwJdUGLEy-gI>MMS?q|7eid6Z z`I=XOLw|?!<@s1ws%%`fz|~uvdah@d%(6W&E98E@(?-)0G^b4S8K*y5L!ZsDD`(!; ztZTO^@5~U`@7Tn_9J3&*Vcvo`a}3AKM1EgkGPoG`Utz2#g_pNm)8Xwi5w`qGcd?^J zl>WQ~3EWznGP}{3tC+`f=1=2gXc&MaYCP9H`^8XL096$^CNbS~b5vFz(Du1n}Xo zQk#Jh*Y+pxy*pdsXv2%zF+MMCpKgJmZ+3*vG+U1LCS8ol>69T7N%ku;=9QPILS%KEDX48J=%8_>k5O15iY>D{^YBLPg?!$T?sKN@lp=w?C z;ucrOeH1I&mnZYK)}AoSKZj|Yk}m^|E1#%)v*FlhbAe95Gu?q=M`#jcK_hO%}}F!}nXbTTkprv+j40xeJRIp-fk(moHdM)aU+Wx0$& ziYp5^8pL{B9}soYE)@CXhkwx3{|Wr9YlVT90CJ$jkG}(c<^K)f|E~g`3ZO;HfvTe^ zYrj1hl__cpK zSnx!RNzRmobj|8|ON|K!?EbxD1-EGCm? zC0C*5#hl2%xU~W%%ucy^(jPxJ`F>D>Qx;Q)QY{^pf9c!kspQt|Dkz17EfqdpMijFTrxz}e86|n$e_U-e47?BR9m~U3{E-J1$g|)kL zm1e7)Pp`HBRiqc3zyVk9PhMiKKva|01JdGKNt4k(gvK}V)+O*Xc-ry{qgqS&L+aq8 zfG?Hjyw|#4*DIm}YJ+Q}3qxgh=pz23?#kTW$?iqe+aT`fN)u(lx_ zuCkRWEDH{6vcQixJ3RDY7=gMOfwvVbTnmOT>l_kB;&)#EM9Y z88&)f%3-eROMKma4oT54!fM2MxTS(Kp^oo0vpV|m_s7p3V<_R04H>c6f+Q?+j zVysJmJx`=J4b7`i2PT_KQ<|J&ZH!gXcx7B;yLq{KauA#GfR0MT~>CjxK2LrzeE+9#njHm0p7&pZ$%X}{#U%oUkQX)T(?C(9b%~Ih9Xva z1zzUo3NSWD11;hlaZr9(J!P3jl@kr}o-1t(QwzS)_sshZ*{uz|^WP_s(zd(;pmw@~ zVNocMqlwE8fHjhJFp|oSkUZADIL#!)5mfv@4TiS0c>~pxjDtsxavoC)JW(?WW$3KL zRW`^nc(=ZuS5!NfPzb})Hko{35VDSa@JOgS&T=R4RRA zdK07{aTIi#J3brwHhoRDnXVzOZm*_( zTXv-Wm!F<$S->Xm*;l3pMd8-VQcwgkN>5r=xqo zT}S2RdnVAMw;g-`uXve06p(YK;rFg63kOXfJ`VpA_Tvk^I}qMT&Cco`sRt~lk4`s8 z#efr#?n7@CSmF=(ca8PPQ{qarXS;i2@iT|6LS-mDB=ej;H?UMq zL)K?!Aq_wFqr*jJoum={;YkW8n}VkPxM7UJj~Z+w>o;x>q*0qazs_j_(b@o7C$@dx zWC_ujr}LV|*?ev$`r0O)vT-(;v_+*DEMa$gq3rQSuhJ?!)m;cijdKEsKUA&LU=~=A zz$-%e$16o-`v%J&q%VII4zfOQ&}}Ap`nv)5{ZIQ4)4q&N1lY#k>x%uyx&nXo2Xu{$ zo%H{E?k|4IW{nGRVCqm2uq&tVTP^(7571h`?o41A%v>@LwO^F>-@}zgli&)D+tK2W zc>_S2Lr?YkZzs=fL(b*8^`-p?{Ks{~$Za@rK^Yob%GIrOX&P-K)AzT7BX{=l9ua0b zP};vDX`Ul~QpmglXEpJxmui*fv%OV`#brQZ`LD)D;e(v}$*tWXD)I0Q&Zk1IHdngh z;nbL$sK}4z6;L7R?{#1?(HTC$9n_@4e2a9%?g37e!i7YJ}`gl!4P}h{+3c;ce`1!>9|>8^K7G{ z?n9KhQYT(4i?&m}@%3{R2SU$kQEuL!Q~%1ws`J{5cV2i8YLEUQ+Fv9f859m0c<7k9 zAkLpV63po+SkkT5NlcCyj%V@8lu(aeKzu(J#%H*H0|`O5lg?-=r`sxL(cBsScmfYs zOKq|Z8>}i7)~NdI;;`SOlba%$l|szD?)~&}B0_A5D(U+CEtkCS-jyRlX=1aksV5}U z&RL8d?wq>SeH)wQLv0X;F!yuD5zi5Z+4=+I^!}4@)czebhxB!+wB(xhMfM;Cg70Wh zfepu~CFx|~wz+gNK)q_~Um3(hR!77h;0X)>P5&N<{$m-$zYOiaMZG!_wj$>N4bvP} zU*Qo&%8iClg#7LP(~UgZGkZ-0p+ciIbJ}H_Q>Wbv{Ld?fmCj29RV~CIfsDczFWwU= zPemVQI5EK>mlLR<8ttn%BO;OrJ-l`cWZqTyM8qEpY}pI@TAYPjIloQNzln#dg{@Xg zr1kSTx{X{>tFVHf*gpovi1Dl;Ipd7u8q5|DvQ~|&RvU-wK@OE#I$?uUTf=-ORxr=9 z$YLO_yh!KjLlhZw^9x{WJ!aRKOIM;AFv4pnKg2VKM~I~RiLG&cmce*ZZoAol<=LRU zm9{8|Ov>PuXD*f*GH`NC|3~TFb#~q9r`!n#Hrr2o%~D4p)Ja1qL1f>_u5lOyHgSueg{CO#@+yL!~y8MbcWt>tIeM7J7s1G-cHM5J}% zlapeAp;-NG|KdMoC`S5#DffTPy8|3mR6Kudh7@eb)I|05Fn$0T#;tV`PTXV7>ErZx znSyGP$9;hR3~(TLy?n!Nw`#Vy*hndR?lPm#`=lj;T8E*u-^65xw3I}eO-&5Gy!=FQ zfm$h96&&ka28~t9!T{+wNy1cD(_5|A6i=5?V11G~xh5aRqc9+#mt8=DpR7x|RV$J( z#^55Hn*)O|0S)3!{-aEZ^;3~)JB)N`+&)~&kI+iE#fKT2-xwPmX9r|0961NPZ7=4$ zIABLn80IMrU7E9O1Z`t_{yeyIS{_}FO@PFFeQw6k)~lkpt7TmZv8kN&w8$5F)P>j# zVn5Tllniq^T1mn*C;0t)M{-WQ>~3iP&e`Yn*XK3j)joD{^^f^(VW;%65v8uB$q&hZo(ld9Zr*6V%VnK=yUheU*7bQT9yV65X1D ztbLd@#)o$gOfrc1t*X3F6rGe;PyTImM9P=X5ps303eSptQo~vzOMEaE^-OicdgK`=8d6 zQ5$mE4zQlYzn!xGPlcBMr}g|5T4pM0TdvU|dpG$6PxseEQu{`^P)&zc;nt+Uve?oZ zq7Sy(3t5jRRIdfie(_YL4~QrCq(VjzYiD_0wu)v^;3|V@;cIaJL`0vHHl=dNaR<&5 z-B1bOaRv#XmsP?{>6c0_4yWvC0QRNcPZ6q8<8pM0BI}K2d)?J{L1H?@q*Hvg4 zMCx@;w}~aFu90}Uh&ifp3ISx=(^zWgo4O$UBGNud``6?S_=G1R)$g{hX27h5wpou0 z=~Fj^^4#MfP`(rFJKlZ+H8xo-q{4mLox$NqFbISo7(Y4*)m3csv_Y;eyY4oI3=>uv)8`8PLCju zr$?3?k_vVr|cQm-24zDPfCZL$fyK$wmt=!4uj@ z@dD{GPNOyS6rdinHVgakPPwh8sd^V!o3lo$Z0Y*)mC3CPC-rOssa9HC&JS2ZyESzD zzE9C6kxykm`D7yD{ugS%l}(Qlm8C|;YP+L<~9Ir;{Ota zu=*u!c}E93JT>zhk)3NNBC&W5Rn@Q=se2bvZSV|CKrb3&zH2=3x8m8)r|fDy>;C? z#67rY`2&rVAHSWy;KC|)M&cfz zy7ndmg;K33&<4<2P_wD3W2GCL^czdKs^*?$im(e#E&ok>?8sqA!

p>Aj9zmucBi)QRF-dMC!{6E}Mb$0vbWR72l4=Mr`E8xmt2~8fThXXgSa>uFcqu>NaqH zSJd>0{_BYMSUe{FL7cY+9P_^q{`CJ-M{H~8Yz>(5`76ozKm2>9@|NYE2%^v8CyF9} zG_uu73YIsWGCmRcx+K~Du_V1Qb#%prxJE6f#3bm~m9dJ3W?h!TB!b9l7VehU)M%m+q8>KHF*x4`#&I=Ofm7rND`zarGl8}Dy+!2qiQGc4_w*ZeeY|L zMHxiX=#0#rx*{Nfzwo=n*qW?l42+K>e@mTHFfKBE)Y#LuJA-bKLKSaGo8V)hAbqWw zXV0Zy9neyzj2?X7JgBRY32hj~_2R)2Wx&Wb`0lp2bi#iA6bLzu-h~nMN+!Q${!0q0 z-<}FsnGW=sJXk~-wx4mmWGwGTFF%%fj<*sdXLj=(=A`U0g_%*bCAtrTdouIn3)dVR z+*~tw8ds)HLJE${`MHON6|R9mdb`RCJzI|yiL7-vGp4%t=*wr1Zl`J^b+nZ|W~yHj%EEn+wggNCz7C_0Z^+aPZg`v>frHxfBWGk$AW_PItg| z>q&ih{GeJp3*}`Y(K~dX;PzSzVd>EyqSX1*gx?T04pce>bd8H{+25`DTREfE4|1o$ zKeGb|C#;v8p+#6YgMM&K7Df~ZrwgFB_2Z86ich_?&UkgpB-?ugTrXKTGH_^47}m>X zjH~sh<78~uRQ-DWx!!BV!qC2U9TXJVrIu;+BuXkr1C{go%dy-??v1zG`%WBuF{Sod z?8KZXHYw9vRUuW|K%WMPGV+IhNJ;i_xv;Mbj_H0jkD}owk1dI39&WbpzY2KA9+{;_eWxZ)QhC=f}sT6dR=f*$R23HDRvtPyopMS(Ji_ zIO;O}d2vScnA+nyLi#o|bPR zRlPS-eAmWAMAy+r#l4edxqf-MHr=oL12X5?c8GA!r*?apJ9qN=emGyx6Ykq zxwd)Rd8~saorc=-;P}9lxio6jbyF&r$t`uLsY}?dwCf&Y9%}iB2URSCh&C5j{i2W4 z-NFKr@H$caIThqQ2?f{$WiW|R?ca<~B|%?Ko&z^G&m3*|-M-8cMq9T0I2}CYyWjuA z+dG9>!Y$i^m04-qwr$(SO52sTZQH7JrES|*rCDj)d2{#v_CCE&_xT_0{ktF5!}`AU zv}VkRF-OFR@xjxh)DEF(fPu&biVfYe8uwtywzhdE-F#5@8&&r01m zFl0bm@r-gS|5(CAg~V&DR8Yzd>HM6W^W!TU(Iy{Z6}NJyC+T?}yMX%OhZ>$rcqpSi=ag-Nk+TQMO@&34 zMixP$L5kM55(*F=YXkHU2iFi%)af$EYA&3xshws=KuKp>I2?_~*9wY^asK3!S0Iac z^+%Qd%*bDSV;1Tnj38*9Q0^7Fwa6}<0g_3OI_5ah)3m4u_ajCf$=LD-WU#$ z+Rv7d!6X;@@e7&FRphJRzYlfBIg&|~1ByQSDB%x;TXG$_=OkFZ@hw;=kKh(uwcSCz zjz8sW%*T<){%Y{2Ou!orLFy<^aRX$bbKesT?HiY=G9mx zdhq>hpbOC7q$Abxh^Yd%43V{7KM4FuI8p=01t~c4kRf|`QEtC|XNObqhm-n7yXQB$ z(kseH{q~rd`}z3U?At+&VQS%a_u*jI{dD>os5wjZd9^3M2Eap|V)X^B-flvDrqg?( zBKElVTdbJW?hOpI{`z?JMr9#s9j&I>GtdA}Euak{X6x4k?D}+y^C-x|SLmEQ%;Ak( zC()Lu@JfDiFNZd0VE&@=N^qPL3}sVixv{4gpqolZhg<}_8yDoAEsGXTIs+}1{O(M_ ztb`{})o}d@yOhRm1Dm=;P?xf;46t z(F4kuZ_b^}RBH}>A5H+**d6ekyTH~&yXD>SfyG|!hPCPVc^Gr)wI{m}&iMHR9%&&U4ssZ}f4}+tb0~&0s-U8n^$YJ@YdAvtDtK@MV(}M z3G)UW@6{`FbxZrf1(8(xH-8l+_m8i(`#;~eGcuek>&r71}_MMyeNyi#0pg3QG(2{(lB%PB;TSub_6 zccAX>?k)$6#r8J>fn!ZIa-vcbzL)-pYLf*`^L#m57d`fFftl9W^}x}lFZ_vjXN4G z9u5aY{NipM8S>RKP;MQ>u9_TH{z85~z`uvYe@IY>zYlVg4R^CL6m-NTqHSGQW2#gRiNwWXMf@ zQp=KuAXK3@Pgb}Y5RWw+BKNnqgHHDOqx`v|9W2J*&XHg}U95ZrsXZVWVN1!te&KVw zO*&@5zSyZ?k9g;4KCVUGk(oa_UK3(uua3fHW{~AUFg^6dfsZg`1%RNo8=XP*(!*Na zKYwIFHo0locx&0_IE5Xos7`gaFLp83w+!g(P{#Z69ghMc@cqu6Hn8~8Z}hsCJz10r z0~C4^A}|equK8D3xv+VB8@lKe-iuq6sj}-VPd)VNa&wzNzeQwtNX(FNdn}3}))&`@ zJ>FF4_f6R^i9`bJF=R@B5L;IZI@dKRB~vBi4g+2?N#3xPRg@&*V9Y9u46gPy;rOYg1y`?w$`>v9_$$-Sk~M_H6I<#`>9;JoOWEul3MrEW2`a z^1n38eA=a0$zSmi@R#wS^fv)Ea}z5&6Ni5VMO0kxk6Q+Wpy|yL1irTc!uXxb${Atl zOe1<(d;!I!h_DLQa#g~h4$t@vGTEOqk+r|mpG{pETx@Zui2HmJL4myZ5h61LRiVkQ zXBWSzo_Czxh-sZ@g*p&iTR+p_($2eV`K&jJ>U>ISgBDAhAcNQUP?X8l|htwt}q1u8gJBsIX- zDL7CIzT*_e&EwKLI3`G2cn`#~*{ZAx2=?nR?!P|&GkX5J zhzASne`s?4gX;bgk$ssy{X;}1;|<~R`*-Lj3}+Bq5=t9Q1Bw)-iQ#mW=u$%bkVZ<) zrLLF*j;HY26G6rG5xbsm>+lPZE6B*%-Fr`A(uKz<22*n37n#hbN+s!qlD#FS8}mC@`}_2X{v z+zNOKJQ-+otAK?9mT5V5dokh71zhu-;4^r6c6|s2hjXShayAs0c}k59y-CD6AE_Wq z2$FQY;#ISSxGcFqpcZ1{BVqamV;ZyH7ep;(7LDff`XqCES!*O?o6}tpY82}q)n^AF4qJr!=Ad?xD z_T#8ABz~C9GGOfbBgJb(mtrC9cS{Gbvq7vL&=*IIf=9DFGbep`DT;$ab4Jx$v8W%v z*+yr+L#!R)_gb0+0dMKqZL%#T4t;GG>&i8ToUhbh(o4<)%w^b5^m0f>Xhkpqx5H*K z6XxkrkizJNbUBR^vyLB5F{RfQsEd^5sE}mEdMx3qk694oAfP zx2{3Vh$a@@sUsbHk``tNzVq#SDbO>1Ve7ho;>*7ghp#5p644hmM)Q}AyT40M^53HL zU%6vnRmXPqi&B4hM@_*mE|g9TmG#s=5$2w0C=>cM)2V{Ww3FZg>tec9Lpb1@dp7fJ zvDjHpr}@T@;qhhN(Mv8%>VUmYfG)fuVGU|6m3=H7B&YO{tYaJOrOr`X~OKCqiFPX>*nHe>*dSz)RrS|bOw>v{Yp&5PF;dr zp(!8(-|96Fy@FykCr$i^oA-weIPs2%h!}%or3D}#P=a*!E@AozcI|!&9=lUk*Wsn3K4wrBm0}ycLwxAGMvhLqSq%%i0}vJ z!JdqHx!1BY4NEH#gIN9$*`i4Gd%mT*yCN_)Sg3rCbEyQyEGyN)nvWdI5>!6eH97Fk z&(UVODPx_v#F~rY+!k?*b;*_~YVBKF>_n*i&hbhtb z^D9&S`AhAA_TN;x{uzgkU((zEmNQipE%!JOe42a)D|XP@jn6|pq4)F7f{BOOP@1$j zQj}CYts+;ylICb~!lzHvK3Q#I=z(A@ONMP%x~*6zjjl;FL3o7+CPc7;jxCKKQ9ec` z>(U@mM0t!MVSWo>0ft_~pshp9gSpT_w`=hrlUna~BiB8&DB-%K>S0NxZ14?(C$-Hy z>OG6y*3)vN#*;>g@CJu1yW-lc9IE5}8|WW_zV46(O6lM1W#&CZuz>oNTIe(=lczip@L7_N4_FsprDc3i_Em} zM>>#6vK|?pBuliU@8{x57R9Y=@3eiRHytxp1!a1*VaY27pE@6hDmFJ8I&9bD$77q0 zZRYo>T;YTg{a(3>IB%l0PF^c7a*S07XVF6g|C{>9Pl%ZwQD^;Mg3t>Ru!r_Z->o6m zXB_DbF-wTJAQ128xM(gzQWl7pmzn+kKy~am<%~>xmh_p8r*>7O-88}g-^r~KbeX-o ziSmkNAajIfyMxcGT_4MRsWiZxdmwVy$$qRuIwNUZaA7Y_r{G_y+J*yx<`RsshXa4^}CtIZ+;snUx+IR zVaMmSxuhQEe7m4p;DPtV!MOfgvRw8@tDf&t`G6E0E$!h3z&WXsvxq7jJo@uq9U+B~ z@)Paj8X*>8KIKh--vTvce=oZTtUqzA@o+L34bJfDyX#;YSPw~dSx+!FuZ0h2R8Ws@;?15yC47APMbdKWLwOWxdqY zAg!nGE_&-KV0HVcdfM6p6Dh_%bti>tYK44Ob;?`m$h;>Z)LuTFWgj#^`_A@ zOA)(G*ehZrsU9#Lw4@JobU`btilz6E2Q%dlyBAMQzVNMjM@iMDtb3~+0m`FkL>7{x z-I5Qay6H3dnl1Y0)0cxzFUI-`QhN$538%&;Deu&KED86^10A) zRRanIIeHtb?>&T*V>lTJ2QRkAR>16z!gd_WYEtb`1#5!iPXndsF3$G(c4OFJT=EV} z3?Fk^p9gKPE5ZH~+9=oBu@=MeB`Z<>9y+JGP`OMi4v`lxx< z2G!COYf{T*LsV`h%x)7x>i-OXPYD6exHaJMwXek?^4RjJxEf1n`5XZFr z`bAaNc8?92FS6TE7s!?hRJTwR!kmO5alEF({sP3?7IZ?y zy@^?-un^S z`a$@$&{!P-5OBgL?XYM=T^LAI+t|&gauP_H)BKA4xMCqXwxC_$T$NOYB}27nr$H*E zruFz)LH|x@%xJVm>%~%h$TX})LWlLeHDMr_g}}JfXGJceq1qZT_Wgv8d#}U#7UON)*By3uJW4Z?j;U*S_(dZlS^R&^}j<_ZWYl5<>PA3Ba_ho~BVl*rzS=-@N> z^Mfxbu@FebMf{xqK@UZ^K9bz5n2AQzlR9&S3Y-*N04)ho6mFda0vqUVCK00M;MSj7HUU*hQS zMl_~T+F|Vaws%_8g&>NsO}E9Whc@ZA?E32KZ0sIgaQX9%M9$JswK!VX99RR~6x*|K zlXhkXJ9@44w&CWJIQp4ysq$}X=*<&)(R%;7qW!4=`huEN9!qUcZY^|1)Y;L;FAzF6bn&^~H;&abk&kGR5-9|UI70Ea4^ z(brr)^h(aqI-E-370FiRPWae?D9X_=(VgY`KseZkeXexXJW7n}+K-&+mPmeobZ-X+ z%RTwIXc$2w)EwaY(W^)Rtxn8;pZ0>RYn0bk$0CLXV#!Zw zTPL(m-DsmHJtz=uv*~fpOm}b;BTDC{3?n6YqoBmqvQ6;(L94BhGH6GtUx@|pOE8%Q zyfW7^Q`H~O{qb>1&(6-yzwTJd_O->y;!s!pYw9`beUXTmyY9c*boSm4nR8!hCHVgz z==|T(%D<|7#g|pr8W)mJiqD_`Jal=Hxze(_0NyGKCDa+(Ai&zcLGj;CPL%9% zwC783;Z=Ji>Lw36VB(a4^yh=jO!0Atm+}gbB+c>{hoa!qrN|-`EU~J+a2F~wXRT=Z zak;|UL#`aO?5edf?Iq}))~Y?##++)pV->og|q=5mEfhGm)U@gZub95a2PTUCxaR5R^ZwwDb@ z+ZZq}o_UCS6*gzL@nqYIT*V7PW?rgWC&@_Jt?dj;bSdT+`4?@fD{_N_GDmebTxGV6 zhs3O}Z`s~50u-Rnj2OW9Ie0O;4%4nW!$W~S6`9IKg+kLoN+12b$DaU`K>1w4IdJ8T zkjIN#dgHivgq^Z8}ue-4RT6T7@e)(&IX|~@j-GxyD0_j zyrhNBwjQ=Wa--hZYIswtYBev0QX|*G(++jml#9>2iw2-{r%X!qFuF0y?-nGOjh>Zt zTx*w*i>FQd@>)Z!Dra`<46hik2Qh|T^3z|=M1zY6qXeHct&-7gF?JIN3}w^S<5)4c zH(<*IwUAdiUbao;t88>wVKI9Ck_i<0G=hJTa?oSti`Xu%b>Y+`v~NdIrZu&lTxSCE zH>y#N@>!~%zt=A~5JI`B2Ydxb(-5LuxXt3xyLBV!it;wE4MGioeXwiM!>G$jkKlP$ z^U9v}m6u?1E(LF!Cj_7V;2<=$U#Q6g&Y?(9*Pm)l$}I%1G5#SJ?UngUFUwWye7HN? z;x*6m$;%y{Z*KTnxkl@&qsvyD)%|(apef6mmT#R(4f|~-9j2vtMrH;EUvmL^hwG8z z$Jors;jgQf0(M$U=&N248|$>+c`QIfkTr1v7)W7|Bp?{ILV6L%zRI1v3R$2ukU{{v zlwo_ki_qPP-*A{f9Z^QWJ9ZVgno0O1Ly$aqgP6$23>cw=GS%n zfPcQuaNGkw1b^R7jN2L5SL`JxLq^O_>*h@tAZ2E?ce@$oLPi8C=cGYyA7Qkii#mUO zJ{2Ewxda^?ypy78A^5ouTSaUN3*4UlQ@rIP>PLOb?`-4tVGYNYgtX`(`+>D*Ivf zYGdDjt9o+y{h-CazO^F!6;71?yKthDiS-vY)ShCL?-zgp-ERFr1(0K`Vn0F3oP}+) za)d7q#n4?wbq~_z%k~obVM>i(PR~cL!o`* z_M&sDkUX{OpuB~&^&cOwSd z+|ssox-dQFj#qXy{BiuaA-s&LG8TSz6OJkS)hPKvy=Kc@ko|BBIn6Qc_~i9y;_Y4d z-2!MQ)a6D6YclJgzCK6S}py3w8!s1Edj3>S7p zsX#5BtLT|aMUXNY?D~PuBIhlekwQ!}W8leanz_Qr(zqyQ4foQk3#cTkj`cWqP}S3c z%IZ9+MBSZYLOU!j7PcuCIaAO}Uq}dhpL8078C0w|`=MP1UUxqZZo4U9S$EuuADt6N zn%9ea-?Q>D0GNm38H0u6y65ZS)M>8JbMLy{O)wdwoMfWXWsx&uZ>AFtu08(V{DBErb%~ zwXoailINAH!6W7712ppI@p9j|0vF=Wv0aDU4Op1Rb0|t-)N+}v@1(Cz-x&`J^&ulU z6LSx9n_kNm!R)3Ghz*DFrn)hGn7w`NUUb8M^L|d}44A~TPEMhmgTl`#3=CiN_`hxl^F%1?$WnBM zn|k+iz<~C0tO=>olsei0w)eABqWxjW>}DpE`L|d`x^36XPIx3zf zD#!{`qA{KaTSaq>W**r>TNAxMkcSOUL+aq=v@~Ob9N=`|wmjky>c-vhqKC5>ODH%p zS8=GKaB($FEy|L(v-@5JtbgKQt!!2y-X=S_dM%fG6!6>+jGfYgDf`N72eVzy=&Xoa zIAxn7du`crv_D7eMgl{Uta~;n7@=aBNP0D*I+2ajHI;c_%o;OrOw0p9l1O~dh#T)2 zRXN##5G%AXL4#(b4pBwi+@c)Auj5C2iI1TRl8bKW*CY;`=hD)$MJPOr=66g_uj(%! zyz|>Gkg7+6BLbA~-F3MCc;nd88|2l0G{9LDYxaEqju+8>+Y|Y}<9yf)Vf4AbF<1eL|*$lcM2_q(iBL zPO@~0CW-G~GpDG9T3xtKP7Zv+4OCOme;A)OI~>jKOC&)x!Vl<7Ex)J}m7hyOU7}=p zwRq@1vqnELqi7t)(Cou@V_d>k$1564qSmrp*>G8qSTvD;2wLd#NqWrOCP8r^-st?>fA~RF+NSbCR;W z?p&9)V?ot${NCvsw<&3@!pkEe%y0obx!|3^3DBce4J zgxhLA)N{=UUnnzFKpAcoB}OS=iTfd9HGy~>JEiXPl`+A0gwIuS`=+M0;;Wgl<;Cnk zTbG+JfUtzsYDvpPR<4N+%@_%AH&KfcEf-o9Zr@9VMhEUNTqGP`%!(?16!NGSEGvTNYC74y?G_v*Pv9KW#ZRad#s^Z}Pf(w&Ldl zX+!(y;nxTekA?D{=N=C)GL9@W59i-zP#i&V6C=QwR$A;Mjw3geAr*6m)dA%_#926t z7-8$#T|KlTLkd!r3koQY!3Z913%SEiP&c3hdLHrjQw7#bX)5j%(=?_6(TGd35ziu( zuL(j3w;a$eaUDh){SO;2$-xEuA=e>P324Z+S(iB!#x@%XohX&JDK&oq|F$t$Md>}b z-J36|hM(TL;N#ukz8Z;d^;LkH+yl9N#AS?puPq?fBruke)Yb5jR~_9`Egz^7safq0 z-2)5ThSkPZqYDnQo1*jE+U#QmeR#s}b8(w*&0Zx=^{sw_CVARcbONKnz6;G~vm;ZE zKwE)KNVDAbHJlp#R9y{JnXl#jkt3W6ranBsINnD7Tx5wd-3*P>c?)q_IS2amnPR%{ z@k@k5*qhx}aDlbVnSno_EBq>$By0B=(b5?@pzUE5<1uMDRvx(JXzM~ z`@$nW(u3c|-CU~{jW;($+mpA99z@#;It(&A9{KxQvE!rjp9>d5vxom8Z<{rHv;Q#+ zUHi3q^M599_5UVN{{MEP|5aVj)O6(c#L&B%dKvSJUtc$w?TW+Mh5M5N^K9m1Pr=UMlV zwAUO+pd{;m*IJKMrpP?Amg|VSEtbTle%;q_xJ2AWR#>bJP6~bU?YjQ z_xJNoJnfvXcg}?kQ+4K}EevojQsw|X-AEC%Xy)D$v8vTt4^?_u#gUWS3P2izd)1ni zkMtjhzB&&{AS>q#n~05u<}MD{@H$NQ`Qp*o_F#$UA z-BIMJmsN`uMBy&2NmhY)Q+M3~INQF$Bj&q-%pST%B=$C@$T=x#m<|f*iv>Vj%{ALJ zl}3Rjq|qq?q7Q)}O$-UcVoEkkM|X|l*n4u&R+IDk>I|y)$HZo(bx-kR9v;~Q5P<|L zfId*~6i)HHEmPE`bg+_I&>IadDoP7@-t%Jn_S47f^DI&Kh;*D15-~+e$_%7rcV=Ww z5y#Xue^Q1nUfW#udL}dz$S-(A9sS#h$itQV(Yq1HES4nic4@$OcEbO5W3EiH zU=0hZmv1vy_3U$qu#F@0N1L*-2CP*tklfDZHP&nJjLH=*;-b+;_~?_Z;K`~}=+>r- z9#{RTt60nFNjL8keiL|e$xZZ`%G}^HQh{pb8TTVF6-vF8V%)O)<~h_Itb0fY+Diq& z3HcM9l}*dd7Skj+X#Juttw^UT$G2H06ZhAunzg=j9hYg?!rRqa-$NY+IO_GMK?4&8 z5V<-G?q;_6wl*EwYY~$Mm9iIBBH$fkeXW!t*lY^v;^nU`(^4n}{0<2e5MoSrvH%|Z zGCB&{oFz2*cA67S)J#S#RiqMsPgmcGC*`>VR-l10_4)d5?o?<&q3$$qO1a5WJv zN%UQ8z|AgjXfF#tSA-0I=z~{vrGTrQEZ_EaSO_krcl+S!ylnGq4xt-7?Opcg8O5>9 z+Y9jH`($iJ`9S*W!a7gUo-We)=&IKHp5{A*V>)2z^0J#(DUnWMA2gX{<4uG>MicUbM&byqqf>ZjpsF zXSsaB{0qy-=6h=YL$2NPm)rbh|EA3M?{8!+|j=R~9Gh>cA0d@Gq%jyQwVgojQ%Da7ysjfkr{rpSeCZFU~Ja>fSb=fTf>= zP5r!6^VuaAH}t0ClW#r6viCX#e#WW=+@Z%$+M~-=|Xk4to!U z0#z^_&xJF^cPuqEpGl4)uG@z%5ixC@D6KkCLcM|44?AJ%}arL}D8L)}ch4=4=pa=n3y%jCP%FKTy9q%^m|2-`k6U`#}_+b#`Q*(Hth zJ$U^HZ^Et+Hb_t8M;H6%8=a(1VNPOXik}5yUU=(8X*NIWycb7q*j+lkI?MIge{k#b zF(e|RN=7ZDs-I>);oN%=6;5jI_lU$bYj{AO$<+0`cKi5Q$mHyB8$rFc<@J=k5>0Pr zfa_QdSQ!aEmWZzKW!cIF1><|O^1%Gg@2Y{*HFrZ52kORQ^p?=zRUli^oJ4P#3uYUQjOZm^gd#C`)=kn{D_FwX0e^>9> z(aFKW#_T^Cv4qZlFk-`I*OYL0z?9}Ad6|B2f@^uw3O_~tW6)n{5Njm@cm^3@x;=Yi zC89e9zVE!g9AEezDt2!R_)YZkOv?zRjkfp9M3}N;;2oNkZWMy;)*BVf-7>UCX66vSsjb+Eu!cvk|0v>EVXpVr>N&5D`AO*enz6u%ht@5vF^K z?Iu|L5MjjK2KQtFrgLocGC!7x58#|3d|ieM8hpF{)Ec~f8#=6Dzeq!Qa$r6 z!!<2mK1192*syXq{u%gcEss9y1{41ftI-DWj!r=}mOHAqK|A(Rjhs(e-@gIof;c&8 z%xlFx#eI z288;03xviKw~c(-@7gwX9pwc{4I%CEaiLBO0gpc zjL;x~#ZA?qhKLkb86@kP99Gs$inG6y^db#4`KZ}}#-s$9s>yX}Ji|PktZbWB#mBsJ zk@;MUrcmu%()^f7IM2N;x6~yt5Ab0X)dKHbP(hdvgQjGrtQQz;2^fLDBYi%qABnSj$^vqcjPA3f zWJv0ZjuUDh^(;Q-)hAaE?*{}Rs$lz6xZa)th&XE|Q#EL_RS$)QI6KdO2qj{>JBA0v z!btqd_csvLjipxSCM0LhqRTzqNFn&iIQ+hrVMDf zR562Ir4C1#Hm89<13&0o&ror-lm@@yxeQ+;Q#B+t^Hz(pETetE2=*PKRH%RkgghaETepz9lSXa6dMpfmf<`>c9yxYvxm~6`GE5;^FY(1FZ{1F zHS~i4DJwKT)1z&M=bQzEBx3|!TUu_rVA7;O)iJB!%eCc~v#*YB$3Z)&E!sD7JAF5p zrrg?y(*jt)7AwxTVC89#^o6bE+J8whKkuCG2-uY3@DZ`!yyV>dF*E93rat%iQ+{3S zGJ2#z3e6KC5A^(d<0^P8_4wS`gWC-S1rJ#Blp2fy+kU!EGL7AJt^u>F^m>Y|;tz3V z{ZqHRPgHE?viQXiUmUN0*OmePx-EnScgqe`%=5A(p6jLE_|<o5DA!@%_`k;fmt3$rk7>&cY=UMV2h> z%mpuiKw5g3jUDNjl(IxD?ot;=34JUDr6f2z^~HdUX!_pO4X)ZUg|C)WXef2PCd-Th zhJB4;e^8U^yCQ~+LLt+hq5DI7iXjqMqNo9x0ftPD*sQDv1nm!sC)B8Wwsy$Q1($V)9Zev8FkiBR zdghj7PWfg%sT{LkJyOahKUf$D*!3ryT8%M}Sc|=C{}@7=2FF>0QoBJ}j>`q0DFIoo zI!6Rh(h1k=?8SWf(d+hYr$Yv=1T#iVO9x_k_<(9{Sr)XY^Lbq+u)ie-cAll=LfV+i%Ph``nyg z$=&nFmo>^sPrwJGx;Yc31a5Ngf_n5QXOIOu6XE^atT1dcy~wMCy@PXQKV+ntRJ{6O zQh|NazFgy+5$`KfDPVMg9HTpp15nLbFV;LhgVw-v)5M}Fll`9U3cIXq)o0PGpV3-o z1zwPzOa@u)V1=;IDTAR^pqLY0CYlJ)Ndl-e#YfDxg|Lk=ldxz@Sg2JLerk-$^y4@_ zXT`-;TRP}`A9sAN5QG9L{TT-(2+$Gh!zte#W&AFa0=Yh_R8%EGEtY;0GrQ*%J2FUR zH!r4d18$%RvO z`nE*_g^WWpKs!y8B`>dk1UYAsE)iY%se97+geOXJf;!p;euHunX8$#Z@Zpo9X;h4b z|0!Gsmf8H^aldT>_A~s$E3el}4oA7A*Eijq>4%Lfi)12++kq#GP&}|N9A9V$@T31Gl1BMgr&3+hC_eDv z)P|8 zdkb~GLXO(3q-dgfZ%o?ux1+le!AZunUitX_4>{LmZDXMeC#j488U4%Ium^2R|!se0_&isQHglo zYI%9a)9M!b-mJ!c$=Htcfxo8B|NROUuU()qaxLQ<71U9jQ83v-}6_eN*I)vjN;Nu-Rdn|2r+136X;EI&-0aMhgIm4%-b zybyK!Ylbm7=>D6lOlZl^A9*10mbq;uXtnGkI^e3srpZOqp}LsgCK6N5ggyf;kq4Cx zt1K5F=oy!d`Kmv=4B|R%>j)0w1wAg9;S)7(w_>nq(clR(LwZjaDyb>Mg#A~C9R|JaxKF7$3lv@g>v zm6TZyleTk}Z4hLXdN1Bgd)(IV$uavTu(O=9HPE|k?NVE8+-jBpBil!&hyjz)J`fBk{21{&$uC#_@Iy;6jk^yEcb+6q1v%>L^+FG$rXuGkiyEG%ryzrihsTMHWIY0!-MHHv(&=WDd z_<&}4eFAkl!jc9y+9?6>>39YlPj-$TF*;e0VFC~k-WKWv_O20-&_f=11)x&NX6)~u z_tl%vsqgc*)Wnpx@p>^KW{-3!JW4){SqGA$3LA~s{*hQSWb2s}|*!S#p}dn!kC*{b%anZV;jv8q$V+X4@Y=8!}N zzp^d9Wk{5|a?k^Y59vByJXCpTA>ssnab5}CnPb)FP(r!rRz9 zs@1Grdjw+57iZBtU9O~S^`X#NybRg=EOKt9+PpE)D$7vNV+m;$_qS=1jm%355(h`U*8WXYq3u8^z7RbG*tk5Qg~ZJ+&1A?o7X`=-@hn7Y-+v4=NUNa5 zB`mOCd5U=F6ZU5(6Esu-9;o<^9sj;cvja+j0|NvbJ*SL_?kRq25#l z_t5u%WVRBL!R!xS{$Bo`_@?(=mk{!>C~P~sYQdl-xMJ{9HpZM8a>9&V-0>W7s8J62 z4yysnZ*Ez0okT?22L0@F1b>0m^!yKXQwks;_D2)l^b9Wdal7mo!l#OURiE>nY=>e9 zlQ~4Xz9?#uIK=?ByydLso?KRTj;wmJ|^>D(LlqyAi8l#SH0S7AazZIMb6}(af%J?%0!? ztY>o0%~u}91t*sd__IiY^mz*d=uM7B55_G2pNX0sY&4zpdg}PK8My9F$q&)kcoV$91lS!hprk5V_$|2)XR-pO2xZ86VmJr&jK_UWYZaADGBxtS%0K*>(aN!+aQ)r7VD*jRIf zJ?(t)wp)AL(t(rnNUg+qJzw5aJ$*4dRBL`;+geWh-F`=JsRU$(7;TR(YgTT$c916v zXDbxVTJhUe?$a9Hs)@tP-X4{uyDZ2jou%v7Aew7Qqj6hiRYGNoAau-zEHyxNMaf$w zzux#@sa;k~WrGa)4iE7Ww-9|Gs?5-a*gE*7zSD zxKt7{Q*tu$suH6z(=?J3qyLMxcM7iT(e{O7J1cg_=-9T=vDL9{+ji1H$F|u?I<{>) z>DYJevvJP%Rqg-7-RC~6syS;ujj@LD8?sUpv?Fw7`@1w{bdsttbdHk>QmRz+)HHJR zilv9Uce08gH_W%Q7-;p8x3`ta%*x4Vt(+oJ%rn1bqL@?AS}of^fMvD67sGeTPmKQt zqGJgB{re)@e@2O;iIItgy)%#!@1)+?IYwZ&A*-a;Q z&_Sa?bfI8(xBcH1x^`MbC0iVy#+F_iZ}ZSpM#yS+Gq{{}v+5~#gqBkB+oF&=)u{I^ z?Q6_ktE90#c3bCoILwn*&Zm5$d}(SkSNXz}u`nC&YY3z^ zgimj^ru;O(&rHI$M<9)N&Q#m}oOFaHeIHEm6N5?BsUEK-Q4IQHt_?9$)l)VsDrORf zc>(_fT9StoGL`J1P=(Q`_i}8<);}VahO?EXGGpQ?vf`1ppp<4>sZ*}M&*NU9`I5v~ zpd_Ws{yezqmEM%Mkw-*0IIjLiUpl^?Cm@%jcNg8(8_%vI>*8QKi&BQ3bLqhCK|M4y zYmf2YCv(?D+JF2F9KQ+J+x+)Z@SpWH{~-nX7Qi8EeJc}>zYSWCFd>1Fpos5y9s$r} z4Q)iun`@aUh~G#GXl0CvNd`OcaN)-uyLbEhDeaTVPB|imX}H6k0oRcr)Rq`i=!213 z4?4Z7`d_JfEb1gobG~xbbu94_MT_p4*ZGVdc;ZFo-cg(Nu)Rs;PI10WbNo~0>2n)j zoPhG4@Ru?#3;gPLI^7Ik>2|D4wMp z3EhGS1)WEJRNPRAY`_{UciM5>w^&0V}U2KLMVP0~&I5I-GJb0={FM zf0BORRGbq4B&_H!N&ip(=zoy@4;}z%4^HZ}{qZ-w&nZcFEKLmuv9Yx{A(vaw^E7_} zqaUv?@Rz&zc|x<|R^vb5U$4YKyY@LLu=PgDt;>I!IX!cg)9DUk%X0?dmiI*@kwCN^ zMulu$2_i2)^I-Z@T4v3ZZDa<{tbfYIrpD(!{k?63rdrdv9|OmbE0&qdOc#PlFm>l$ zpT>*C`kvHd-pL6Nh~D&+%)M zO;-Hcij$)E({g%_XzuG^HtCk;9yQDyn~)=t#4dI&DBx*234yNS!hTBa&w=nyHiI+VE2_b_le$v zKdhVftv33Ohi?LX3mKUt*1gYL)v`4JT{fa8(+}^GJ7$6A`?_wpXQXBo4IJ5?mxRoL zg6+sd{i;1aOj^rbL$=4R|BGJ*;*KL>o_kMDVLC^7bEDnR&*C%Ua;G?LeEzJv0BI|l z&OSE}FzIaeXL@n_KtG_u|J?~Z8=}o;0pH^OvN`sjHK6~c5dV1iXZkswtPlr8&>EP! zDM>%9mCmPnvrv%q%5U^qQB^(05gVb?z~-@vdzHZ0>cXWY`xc)n&36t--h4FSB`|0) zYN>9ELf7r?ydNC@I;asSDq1IwYZH;FAsc=W78>u14$ zLM88x=Sc-zwhT-?l13feMLA~gIpO8xh11gN_(zC@y=SA7n=9G5s0__WM_ZW#)Z7i{ z5%5@eCJ`s%kVQgW5%31zO?V^QJwl)7>mQsl2Hv{<1Kp>>iF6SKInfJIfP&K3Wl!Os zR2LkW4BaYHr50jQya~0_VDM}tub}+YLpRpeA+*>(cUbFG!isq}0eu$jk%V!bc zrVD3Lf54ZxZtj9B&IxOc@Zl<)QI+F%%FM)rFnxRq#`5Evn@h@i`RQ`8t??2A-0FU!H0U@D)cBoRrL1B*@?UUyj z(2$@({wl2XfO%J4a{8OTQ@a{IjH+{uQiUX`wX7y4yFu3-W49xBFcv+n`4>@(R)A5Q zUk=XHo2jhaA1-QY$(Jp1Rjm+`3QH2U--aj~HLR+N*rIJZ3tG+vIb@yJ0vI>l^U>b? zyniS@c+WpE1gNLPdfit#C?g4dG_%SrOuCBzvp=}1SU+}E9!<5DIMuWc0|i(`24zS~ zHN^@T5%LpHR{rphi7n8U>A_BTJ{k1@Udz)T2|s4XAl0?R4ADZn2!AMR&R!)0RA@@C zMFM;Q#AGVMO6eKorA5=iHI_OU-B>H z`@mCYdsoSF zR-=?XApF@gHX*g%>B~Yi9leB^fHvNAC0Q>O?6|^Knvt5Lmd;9TQ+KC+JtQ}}$Qom` z&f%&%T6`Nb<=gNb!nE|a%KhlHpEqyZ`5Q5~amVEm6N3YZd?<1^6oFSuOKC?cQ^8R` z8K0y9U2QJ`ySAsvSWN~xUdr@bpv@bi*g9xZG!5r9EoR|;C&qkuP$kFaJSN_;w#)%& z>gB--R)ge&*XU|WMSFd3miB1j7dT}dZ6%E9s6Z&9`g<}whKi{$iHUA9gyaBrJOm@^ z#2t&Y0=vDSu%LcyD-~ChbGe^NChYkY0Bfny!L2v}BElknvvnkjh@ZNmFN_eAnEYGf z30(b@oh=2kG--t}i|vJlFPW+;7D@{aV0dja^DXBbB((Wct+92@CRn>n^7VL=AJIt) z(C@wiAwj;o1$r?zADXYZiq|NrUn5#=18Xuju zUWtkfg6&>|7|=OBm^m$nB!cwUZfb&pd|w@=iDc2vl(UpSO+8(uDz=sb`-J0#ageHP zI>8;I%UU4l6{ZX$m1cD+u*?~D$3>!X?1HTM7IONweM$as{TVQTVXLY~k8Htau?6p= zGXixb2{1G!^)>gYtcQQWmWW=G>4(xWpLgsroTj=tZKjb!;95tZ1q^B%n?IbMKkl6= zV`qP$c70OyeU;#-7T#UwWkOgzeiTA{*F`HUmCA_K`(ClBe*V$^O|VwoDGLx{h+JN# zvecI_^-yc9Tp%OD!N7ZN%ArNjGt=9y2AHN$nIa1i>Xs22mm6;}Xi13ZGXKNzM0|Ki z#xheZm?mae8v@Th45A9tyxl)7-qvwi)oxzWSTltLiru}9=v6fD;01oMZiDo0Kv2YN z9SXBYN>K8YT=nNxb9@ew%h|LSGOZAdDKQvr^BS+Xb}wOe&}+uq0T_1vL!yuM=2g`FQK8zb3@-9)9k&F<5U1_RyO#*gKwf4Pr!NPZZlTdPLq(qaDt z+p$HlD^piaqV}g!*pJqpghr_YecdrTeZ)OI_}ACNQweJ>_ev9OtadYD!}ht$k%o{O z8}PMSm#F)f+re^=E0ybbyK3L_a)=musi$2*ZIn>(mq7bYkkD{CG)QUWBoHBrXx38~ zS93LPcQ|hX+*ms%ysuzYgDbN}5I&s-o-m)cti~)q5Aoc0!GABqK)U0D!Q;=Of}uKk zM|}wgGZEq~Kn=G2eSQ{=DMl;`Q2YWz@PhiKSLhZDE1a0bPSTPY{|^u_udZ@f(~19E zHc8yz!PCw3rP@o!lPlM}nwq8;zQtgpg+w_mom#}v-O951V%(ktTVPq9$C=2^_od{Xqm^-Q#58HMY*KSh^&*ds$$0jcB;;*wFeq?i( z`mqVhsw00P!Pd9%#YUzRZ+>E$x;K$C_-Qyunv?6ClN+k*I_XFU;B@80)rZA1`E1o& z-=r_^N^NJSIf%8o;_Wp~#q@huNkiAoL zle1&#Si4$E0ry(a&0y&oXCLgdyj1IZ@%&?ryjV_VyDeN-QKe_ZWes6i^3_spYZ)xI zEPP!!XC7HD$O0$w7~;ZZTE?|SsM0O^6!gNzbb$s?#f9{aqbO4w*=mF&f& zujYM;L6db=_VzblU)!ln8(;n-B0HXrG6Qy+sg0kj4P75Qur6BatJxT}4KJ0qg~zx= zvbcw2TDFg?g=^>p*AUL)ho-M5x&!R&E(9@Z2+k(1`fH3{dMPVk@#6S4ui8H(FUN}x z+079b>sht5Dpxb*(RL;S`?HS`<0DTQK^54saE&Gv#|Y;e4{h1J9mUtOPqr3os&{#O z{)~gGil^_D1Q;ruzDldY2;0@3Ei`+n|EMbNK$=orijgqwQ96XaM4~bDC z1_pAk>O5Wo74V8-M+SYrHmoauet2R&dMR*l0EEt);@1O%Wl+NDhimo`Ga#9&hzru7 znKmG~S^Wcrhgu?QNYtf^f~HrjV5V3+eqfkO!IKh4bSkCjHAhMCkq0sw=~H^GNTH)> z8Gx{Y2P@Wz`clHtGopIe-Z)+57i4` z?o5ht(g_22E0=x=o59rnfoJBc6$Qv5&nO)~4c|v){Nfu>LMA%5KSAn`Z@`9RYL_2+ ztkEFaq_ao<9rptPL-ZIhv=nfNN1r}eJ!XZ9A}j}OdfqODn(#U7L{?SoY@(TPdGUSX zYd-uer0>m-`B_L!>aNmJ4wucKd@L{%H#b1frAV;%?M|5O@MOaEHNFFrD@2$vefH*^ivA-3BY6j3yntN8_J7 zWBVuMyR``ov0`Q$XEtzN70z+_@e0gCE7;(qc?lgqZaM26gpJM^>ta0dc#Z;s-GoFqM%1hA-wYH ze{`hJrVGNsu46#BeKUuspg06Q7(%fCLw^DivLZL6AV%0SJ`Ed1e=>ekrJi$>>pmUi z(J5xd7zW)w(5ptRA!^b$SfUjzYgdP3?sBf~(Rc}2u$!BKMPuekoh&BLM+u|11NZUD zhj|RR6BTLK9ZyKr&r5hj&VmT|?mI3_IOm~Cli~}mR1Q}|xX|5@LX;iC@fAdc7rv_6 zC; Df;bAKOGUMys1ZqZ^+LBJkDC1HNxKz?Vk9qyn*BIE6nJbdfRE;^hync>a_2tHLJb{Fv{ zL%7>_P6kZ0=s&Y}7T`|txhEr0HE4u0&`RK2RR z3+@fPbQ&lp1i2&qA-@K)Y@ai)r8WioqEtIxkI( z+gegy^nWKZKa(^ije?URr-gXFg4dJN^^R~_F%I zlay&cFq{Tqq&!bH&lmd+vyujafGO6?a5~6a$SAR-;L|B{EX0_6pGf%N3jen`wdf3d ze_k3|9o^APV6`}PcqANiVo$8H=lI^*%)7pXmJ7Ay;@X)v_+fajF09V=d*P0H@3pVQ z!BU{2P#ubF#J5aSlp@xC4_w96*!hP}*}E`3PMChki8evZ4Fpa~Rskb_$e+(*gjmV@ zVA*+_#~%!&)h!-1+SYJ^sI5bqw{OQCa$i0T%~+wVYD8?EhhniBcmOuJdb#XYo*AgArGj)W2kmN69s=0EWelq7W`d?3dI&ional4<)51uQ7NS8l zkeeC=24Y%9kS6h{I3|`z(t+O8Q!#_pt?fe6NeW}q92qJdMj?gxjEpoy3({AD^S<;} zA)-DaX+Or9UJQ_EQAKEa^F)NfVC{#Rgu{#faSpfrDJ&7acO04EMYoMs4tlzDhv3o( zbRiJhKIp0kL%I zSdFv-HgAaGuSz@dhNy2<<%Lh=Dn`b>wm)%0g|7SW?-$fjWN_mWq8$r+bQJ0LAI!#g zOkc#LPRvJC&r)@o6~B#w4E<{5zWDYd^?Mn|i{3S~bO#ODyU`vxR4<09dr?y!2EFg% z^Rw8e$N9~ZtK%l#YneB0Xs@y{?*>7;F?l|G#!z8TN(4?o0!3Y5%5x0a7B@zxq^4AvGwG_)6bJLW#J@C6?e0qci;(v;V(HH(*k zrC<5U(sal9VyP`*Lplq71~kjZSE3rMuS3d|tx3z`4`SoZ4;yo2J^qWq>_dh8J>_Iv zA0I7N<)53fJ$D4fy_>pu&%|R~uKmn7rviGEj30jSgth1sSCb#vFvy2pGv`d3E2q0J zqQ6ejXns3?1y^rachLDIJ;V5Oyrf(A@)%TnXR}I>$S)?=K(`EhWu7QU!sTPG9!mI%RCYY90kU2}wYa;JR>o#Zs6K0nLOl~xFE`f$?Qx);5sB2hB} z*ZQj>|Ev`Fj4^)$HD>ALV-1%+=e_PDYd6}jAcCWdj=xljA$R8ZImfW0Ba#f6Td~w^ z>mc*8MrsuPqphVHjALBuzyRsTjE*2Z-4)s0J^aZrg`YvIhC;+g=(PC_B2&aiR0CS` zxx%IXsY97PcL~;NnS4(j$~itfuc$^ByqoP%-nN{Tg9VrMB0<8^jTfl5QF}yBOcxb1 zFo}+w2{t|rkVQf+sfy{oSKfRr%Kg(I7woF>RYIVzKV;+TEWYzsl8011wg+bu2EZsl z`!e}VCpm0?m$DARH6){Slblv1QzWFZg-L`QT1{bM!m~)9V_ov%!SoskCCjpC8CxG@~gcBvZ)yBIwLm6(U% z2v5w+gq`P?FHrFgA|s>QaOs^zsg^*XA_R?khTM>)(8z-u6{5eq@4<=yKn3Tho`!vC zB-e;jAS}LtRT}E-XuVmz&-KGc=x*drVTJ@T368?_+!G=tsYZEEcAxmfp>`e%K zX(a!!uC^O2gIVhA)XZ$aHP}yci+8Su$t)3-x=l+s<0VFLaYv{#BHLe>EFqrvhOE7whF@fi?9X}0 zX!0sHKRK~62FBGF(*zJ`i=K+bCKGB0W7?486S*eUc@uUHn&~|N4f?TCmijHu%LRxS z>$e~bB<3AO{-la3trRQT+$i(y(%ioCO)RuQP-jjQ*d(?cMo>CO@avuOhM^XgJ?Si*_w)V{r(c2n8!&kv~XprSV z3mZW9r=|Y^binML36!f=eXRag;4A&5m68rQFVi$KYV|rIiQFk|z75{fUcI=f)W$Cx zXdozDJ8`CsCUrf8BHRk%TON83)={2R;hcijfnZ|u+kz~uxr_qqJ@=RAn3h7<+Ja?* zpHT{8O8*MFS^bmoI!Gmn#w5k`v=zE%(-G2 z)6B4k9WljUXXc_iEtO*)JWz?RplV7G;REV%`V& zHQC=by0E+$9m7;0kl=TP(!U`h#-8fH2390%~tYrz^;VsT6IG0V4_5~MI^iK9Iw zy5}|UOdMU%Xfa}Hq?}pSd6*$?7Omd3RAu&jFxzSQRW*9cjK@VZQiVe)hA8I9QB@0t zsso}@Qj(?4vGQlCO13775AxoEtTp*h{hfT?-n*QiQV62ZA4^blGnDm$I)7dug?Te9 z)4&;I02d0s4kYbZ*zuppQM~UMY+ijQH3H#OEVeD;rhMNt;Ldf9YYDtapLEYNarFCj zhttkP$~5pv+~-%QI8Fc9ap2nf+j2E$lAKx(=m`G=Ue-SW|SK z1)6k#@RJJ~+1x;0v;ST7to!y__Vuwx=!yZld8qKxmhMVwvt{^TbAID{c5Y@JiylQn z!ws**LyyXB>G>ATdJVCl*i#s7i=Koq1-!BCT|}Xf4W`c_86n;SS13R#lowNK60Fgm zZRK04y(lRnjG+yT#7Gr6deNU>qPi2m^Fa29!|(XFbCZZcp=S_Zt{oDjr;GDTbWj#b z?ZShNBrAp|&05Mv(2OX~e~_t4z$L8Wsz<%rU1lMBF5Mt19;D+FCC3ciFoYulh<}5+&LPi!m$Y=2N~jv)p<>X_)>ckwFj?11(`#;#$lx z(EAIlQbmZWqOkN&@po`MnrTe%J~1NxO|#8~6w48v;3kY^A#NF4)Uz1Rfdwn@MPBqa z0(p`Wn+r54>9*Fn4ddSIo>1ON98SMvpAT{N;a7n{7rr%4u;+{$8J6Pos{rB2PC9AM zuxi6g38g2#r5f^|pigkKwDDAS_}!aon~2`}!tMwsQ&9r2fk-m4aJX?3&|wN&HvyIP z8wZbD3$(iV7v0d%r51%MHYO`$S1qe%&a5~{(`GbF$5kYybZ|cU3|{`9b_5`ZP9VmV zzc|{qVAQWKbnBuRzv}(a{vNrk6WpRxJ~jq}Q8Mztsd!MP%YZE}fmBy|t$$IZN!xKP z0CBrk-vhUa42zgpMJ|AF;fI$cvGH@i<7DN=w`Kuu_EX{Y>^5qy>&a&>>AkC;1La30 z`*2*su7rd{Qi02CJ13X-!cAV=E4^x3O=XAbX>h5HR@3CmcNU~9v$>_I zgGLW`=a-{_lUMssc<0P8-&)^A?dRQm9vONq3F&}B5*VpI?2gt2bG|JL-YpBZl8NFq z^Y)l7_|A8J*HuF2wYiGXej+b(%LuFJ{I;9pSRmfx!m;}IW z_=pFL<7E&TE6Vb_eMpp9C7?;7Aj!An{j`un06+F9X)cnSY>fP-GmT;7d)=?tP@g_S`Z*21`ZL?Fmk4G-W!(`k<1)z> zZS$pk8PTD879OL@bR%f%C*2JoZIH66`?iU5c0c;-rR^G2FwHPB!>gQNiYPddQ6 z6L~$nme+PK8aMbiNr5o)f|;<#K^`{iE$~q$ny)GEH_6&1`K?>V68Ix)C75kUPigKm zLt+{G9@t_YqtD?WSDWG3rh?nTT0udo{k zUSQ&CqVM4Nw;`^$Ug^QVoi+2z`F>*LUIo`>5k-lLn^>p&Rcg7?fZE4R%1VsyZDa3` z&$w*;qOL|Fl}NeXgFW4er!j6j+yC&Jy) zOACI#+(7r~#K9#?-FOKjdnF9A!*@6OLc}$6z|Xr0(J2Oez#mJ3)O)|a0E`o_U8uKB z9r(&VBBjlq0rMTr?YlCY>ia}+^lHzB%o-r^e=i^T${+IA0pI=>$^QGv#{Ui3bOKJ9 z{QVStaD0#aZ$^Z$BVfgZ0H-zk{`W5!3@^Fwo`(u(-_`SlA1|q5XcaL|+8aNf8>)P9 z-g#mn0wmPlwgnY6&5mLvj;AzADN*H7y(1&>AvwE?|3qkscuOS(=D-#jw#XmJu1U=> zdxQ|Vxx>`*=7;RTfb?e-x-U5#L%AJexFq_hr*@(}Ze@&hQksv@I?x158%jK-s_%1! z`6ZMcV0h4#0ARMqHfd*1rFj!5C*e2-_@is<@zw{}Fq(}>d*wghU&p?CZhAEn9I8jG)wa6Z14!9i5By&F@5 zs??VHnL}1upE_B4T&504{c1M`KV+#CIagY^!D>cw|;gTtu4k?+s9&@Y7M1 z!z|RU5^i9;D$sPldV)t^GIM(88dQQk2OztzGz~@%XyLe_+MDm>kKU#TaQl~*P4@UH z+Y$ql#xZml#N{>em{M>d`7BpLI0}ojcszZjSLXx3DR;jIrJ_HEIOfTvAve>c_}ibr zx$^0a5BDPxSlnwnk4`9^PnUqpF}g^?0%C1rR4gGUhh)y0%fPSaU4I6OnDCP}fO{|Z zdER$XN!5g)ZHooaAWk*Jt&tU=uRCWB6K6ys^)^_K4B+uj_V^_u5izNl-5-kqVq!(X z$~mA(@yEMB%c!m?57BXd_aplpdw$SMZ-&XdwJH>L*q;>>LYX&D3Fa%e(jxYv?X+Nk zJvBc$2)vs=T(;M?m9)&!OZW83ZjbWC?(6OZ46Al^Pl&Nq5%$6Kfflqb{umagSGV7+ z6i)O{%(&+Rc_juHpSiVs?Fc3oACOhuYY{@FFe^NTN3THzY%aU`B(0&rf3BOvH)K13URI75zPGcV0tW~rnG#VIO}{z0-;_v6``j2P&l`W z^0pOrKJPNfy-1H2bU`lJ{wlw9@*3L;7>Us!Q8Rwz9a%-3xaaPj*UKW1l!30a>HX-%z5s0+c1E?yJS!(qHhrCTTdfR&R&Nf{Fi zS2;brr}3LQVe~uyIq(h?HklbKJk4OBO1yc^r{Pk}6XBv);CMeau9LcNe2 z|0z7A=Xu2ws$Fd=c=9AfQSDZYOvVsNo}!lyT{`?Kqt#^d9$4&gCjI;fr(i1d(eDdk zYDDle-ZRiaWvuDb5~L(xr5g7QYo z&dhU<^W`b!(8DMi?J9d_m2bbA?_`4=V?>H&;x=Nw5GO*&M1C!&!(7ZSyTudv+HKYU zd^SMjvh&ovt{m-tnw%Mqk9^r(loY8YO@M2&6eL61hygny4DV7NxyIflk_Z`a_fj3$cR zJ!}eqTyVL{fa)@8>}_zSA6*aV?*1NB7!H$d2Lkyq0Q~)X_5K@{ z$p8HU!T;9*+S)l=m|7SaIs+q&|LF}=fWyoD@Fgl!@j@lpbr2gxS-jd3#huAit2J8NldvW`{cGM4+~xbPre zo7UdTt__GcLmiBtO~R?>#BO$H{lvd}O}2ePshq^?L>fiVdXJHX?xPY65-PpGPrRzj z)iB}@89jzHF+-FexqvAwwPA$Iq zqI~v)=Z} zdfpbB%E)i=_(dQD&dCd>;vyjn=WecaLwP87FlKd3@*3HqWKS!t=KI!WC=N;GE9jza zylFg=m^^6A422Wj%m(o+AUVGGH4;zT>G|{JjO9txt|u$}f{*i{U%o+x#LT&20y&tk zd0mbPzW3y5{V_2uix5eDs$xt#T8mVohE7VY<)VvWd1+VW3Ua6Wr%0Ly4ZI(`;1zR; zvQN^R^VMMZ$WFRW2_g9I7rNiN=s$8KF@5PdyBpg;e5+eeR{Ha0!maA|)9XlI@%|~Y zycd# zpKgseQM*Y>{;}=hx(V_;G^?Sr9{Q-6xYd4 zc$hK5imbd8`omwcsqwGr5xewI1Tk^e3lCv!V!ct7b8H<>MlWYnnl*F9S1LJ5L_Djs z`U#P1XV7i3pnPmUyooH_o9KxP!Et(`J#C%RMyEPNT+d0&&RF*3ur5A_!@M zB@AfU^npG0e=ivSo7Vil7aP6fdga4_)_m%m8k4ZU!t-ehusf1oYmHH1ti75j33Fx3 z&JIg98uQWZ^Uj9*Y9c<3BmrRg3C=(jrBp9E{T@bg9Doz#SNysy*f4D^R#V=_cl=U^ z@>2JKn2Z0W8&yr9a{EM(klLdh?;*_}0(&7alK#9&y`6-9w#0D&e7?8{$A{s%Oi6yBDvIyvb3b35^6>+3DOe##0_#OE6#T3sj@O^RA2G zXJsc)WfK1~5&91^aXS}BBNN~%s4Wmw&d}n&R>>;W6zz6_Xy&ng13HARN$BvD8+d5Q zbSjkcx>_llm;IJv*}qA;p?J)AxO65de5C*^j))U?j!>L>9@CkV+PT!9l8q z4w=Yfn43;#%Lc4IWu}YtPHtEgj4uCTxv}iP#q<8WiGk}+gyX+yl%24_V@zbG91~X%A-la5J+uN_S$YpfgO(?iAArY*)|C;B_)SU%RRpEChG80ZN)bZgi4ROyTn)ws zfYos=hGpX_E{R;s0>j_BQg)FOx0CKpIOw$dW0Bp&*^f%&TUgVXs~HMfT*=}+0}Bf8 zkiQ=5UXKF?2&;$OjKC&-v@6N*l5`+Il=(U{GR@nL=69MH z?Uk5DZl0oSC!dT_7dm?kPu+F%^_oTpCtOp(sK5v^_Z3qJ#5Q!NFw;31%lvOYaw>xE z*-c~8wi7dPhDDSEz%T+p_Wh|o2!&a{I&aTg}&`3y%2Z9832 zo<01NY4!L;b?Zs5RjLYMrz`PED-3M-@hj5VXZ09yZCX1=X~#r2XqTb2SmK?aTw%>eXU1jW&3 zR|CisOJY^jd{{RpYTZ-qt0C4aD0!5h365|di><~aIv_M_!hJkSi3U9BhH6^eA>qV; zmk<(%qrwtrpg!HioPG8h=bdX}zf9gYyK2g;4zPM`z_6w?l6=HzG)UE5iEzmgTq-yNd+8j`f{N42T4PUmGWQj$K2;#!hK_L?_Pflxr5fZT-84| z?Fn-d#EB!%-5dw>)m_?V8)lzBKL+2ThqR9(c7E25(HG{q!)fPzGD7z{;SzbIbJf<1 zCb%!3q-uVxkIknljK`f_r|U6#OfSI?n9Eri9|q8>Z`_4%=kiV&Ml2%c1jdJ|>D)q~ zYuH-nh{aY*t!yMt7bplnRU2|?0qCr7F04&)y2R&*vRl72&u~^wzrN$fyiPCs_Gs#` zr9f83R%)i}=;{Oj*qOEQyU$6%yj#8?;qDE=kT{J+xUJvLW}QpcH}wQ%U7$9ew6=n| zZHF+fNNoYWQI}c2Ol$sefJ2}^%u63e87EX|b7kD!M`JV~vCwwMLX(JvwMU%U$y8lv4_j~`O0E3J$z zK^N(2Q23v>Zv-&LA_Q<9i$Ewho~MZY^P+rbQx97bj=*_vT?lI^E-Bmgxi(b>&=!#X zz*z}X#sLq5I7(M}#YC3iqZ>d0Om*-M+PikdeEbn%jR}7ht2saHa&abrpc5KGP)^BL zer)&Up<%`xGy?BhTeB!jPLoF|Du5Qy1#Yk09x>>5dwV>j-5&Y*paw(NZvy+KBqA53ENzTC18k8SV&KvCz2pdGEZCftR-?1M^L_0(cgj%7DA+$25pJSr z8ai7D3X=4xX44ceakan>g8mJpZU7|?@Y@sw}s=#X(1&b+p5o0o) z6(GK^{UM5mqjS7&@H?)wGcti5#>hNHExQ=joOk_!u$kA=XW_xUl;4}< z5s_%lsZY(!ogv;(nA`O#!rPxMcbvGjquS2~XRf=DZPc7cFKSNt?>@Iqy*W6lVIB*- z@$9>Kuk{hRG2iRTTGE?vroaBK*|I>Q(Yl)Zmj57uZ2FWiTP3wpr2(D8FZI~hQ~j!O zHO*g#49HN`cYL;sM)Uw8>X9X%rai0CtaRjSl8|&nshl^6tn`NlB z@p_kbq-*bIHgoul-Hs_id1E5+!Ia&(qidvUIeQlxb`8Jrk}eSU>%Y@}Wo2?0U=H!+ zBzb$l!x%1jsM`BUTXL`NtULZXz_vY)fyE_0nBDTMShM(z`sG2;&pa)Y(n~YL0;o~t zs#Zd`M1!CIYGDM$2m4(EXN)m`3G%;pv;SG-{y*L9zY<>^8{kY+^N002oF1qfaZ@B( zN%rREdKTml?j)B43h~`>Z*#b?I`fPwSqm|mwY*_J{rGu!a>eHT!Kw}(?7~zJ_u<eLPkZJ69!Jvu`ZkqAbg6hX_R(MQt|>`{ z{=PIf>EOSrNS_d9$fNXuJwO6aVpMmG7V7J-BY;4Bv%&cAD0b_^oJK^3n6_wQi2ZF6 zLMiflEMjrqPY1u|XxV;t1QI5RbmZ_W^icY3w1B%FBVzv_hcU`vTCsALxy+S+kh+7l-+sdg2P#1ta#ZG&nUXQ z&m=texHrcFJ@2pP0UH6Y2PR|zCLfyc%&&uD-}T$taP@5s>i`wnxEW^8P6289c(56< z01J4>7lO(kM}c~{M1O}FE*|XS4x#Qx@5r9H`3i1h#pnz+6s59f8(X)FQ(a6>t=(qR z;1mY?_myff7M_!dv~hx=^jd$7{1ThHhsR!%zWiW<0I)VTFpgyr=o>t`AyBGVHpxm{ z`>w}TapWn;qfc%q2x#w6NKB_OQ3w1%iS3>;YKrZgKW1aiF?yB=;#Xy0TTxK3I!Hn| zi!Qg?;I0D!-2OTaMHKcf;H;sIRR)?FrBMB$C;QV2eJgAf-Du;Y#}EtO;+qx&O{ih? zq1{7S1IVn@)rh1#5ext%yP5}A1ieZTjH7w@VmVS&aTwJSDX=%CPy6o0Qn z8xhwY#|2~1fJc2NfsZP~Zg1yqOnejSNLU|MM51rr^L{pesKc8`tR^w#F)4=u6;<^# zL;;78ubz%WQ^Ne zP~jR2B-YTKO6mKJs_k*mxlhMP$32Voi;6wkc`I14HOf?z)+>*2Gh_xx+lR}RuJ6{a zc5BhoHB<7f;uR0qQ%v1|H8wSDrTO&ndVnD2y=#}GnEzppEy(X za=q~btE3BG$eN_aRslO+F#sud+S@>VYo-RC@XN+>>=YC8OymND>?hjhig|S{1Wa!! zb~GX1Y2g;zyZf|E;p%l?^NPZJ zT9!#mXmirDEV9zNa+0I`^zihZjWMym$LCKT>v`tABkrr7iBP6amKJwNk^eMYQx1jP z*_&C|X_uITScO0EW=rrozTtGOhS}-f)rk$dq3q?jQPDYj+vP+cn)qfKdUqOTY+ z`Wzw9j4YWJLR5ZA!cFPc^t4O{KN@D1))YtPX1wg{Louq5Hr}qp5w7kZ4qnoQ6el3C zj>0LiC_VXKT{b>j9LHBU_Y)Tsm5le3zDcztnCT zl{m&VHKv+c5{7#Gx1n!7I-LW^1q?lutkBm~l%kd2)Dx~0nxPjlr_5U@$2ZLVoiyNS zdAR32*OMG7$T)$ z^ctQPO{RM=g9Ap)Of-(qThdvrt+C6>DEOX(rh(B`4qr_h?z^I6imdCc8XRRughy^2 znvW<74aK*)`hyUYWfU@kmG2}MxW!k??iC!l1j))y*2E@+;_-}$E<>B62p?53T3)rF z9MI3oesdVY>}4aDw_)@>>l11Ww_XI3X9VVw;O{m1LKvfiX7E;$4&^0P>b%rNyhYP# z^OK|~zpbrlK(wwd1)p>~C;PrZm%Iy-135t(gaH?|#@QF|6_&|k$H1-is&4A&Xq!5J zW}y*^7oG|&+NBhpCk%{45Yyj+#vSj+;;XMew*GSGvWu}Sk}PuQ8Q=sw#Bqff{iEa0 zQFC>b`UC;XDd{Qw^hB2nt{Hf_#^M1qcl?lb*=S(quE(B&6Jj$IK_g5xcnsjiHUWmd z0E8C7EpGAF&vJExJvrS($~_-JOoy%r!#nn1pfr^2C}x+LtI+6=YV0n+$vCu7+fzwx z&w&w`_0?V;A#+AOS!U07psYH?K{4^<4m{YwJ}u>fN?ivW+F}ltRe5kmjOH?zR$iW; z`@1@(=3UrIZ{b5{?vF?D3?Q`}bU;&vZ+{)>#x}FwfZzDIDl)-@M@W*Epq8HZN2XVWKHlP3Wu|C;RLux4bsjm-+bGgXU!NpY+*9v=yfi^Few#P0feR& z4K&tw5x2<~c{w=hfJB@r? z(@NIf&Dux?h5D$Q@nceoO3Pz|s{=ubjTb4U*6A0Ok&c!pnS&0JxLK^;djs!2f#Dvy zTtOR=9IjiCjW}R_)4rQ~OA4v4Jx_NEjjpbe=cRx&?HEO5%KXCWiU$6GAKrCM)r0JH8K*?Qv+L4-!(9Hc($_{CR_2~V1kc&cI|nF zd_z54ioE2zov51jXxgT10&2CYHqU%=@gYhX#Or7`eKX|(o6QKy`#C8+Fxqs;4KeQy-W(~7X$1R-N!MGk`qs$;Wr&!&z+uxw!Y7V%QHwU@5`4TSbo|;2 zfv&7qrr#%*i?*2Oj4Ub93}k{h`BTDhnRLGCZMQV6NM~jf&P!+WNaU_0StN97k$H9U z%c0jPx2gU)BWI|xR710PMe*On;gB&fcr?^eTE&iSMfKYvb%tSIXTu|D!r0 z*wD9lJ8cZnlo964=;9WoxnXK%q}fqEbp@*U>VI1Zlz(&wGzet6KRoRQ1ywYET)$4@5H(~99vTs?0eDMjQhPF(usl4IH`KWc^hLzc^AD$_wt?I zqBkM1VKh3iHM<`Pd_^PL*bndAMtUrrQ6R`Rm}WTop_TRz(>10GvR2YT8Xly%{@TQQ zDJVcpGPxYG#82^JQ^u4Et^O`l8XwV^m!bMs&hQMX(fK2iR<14UvSMa2Iu)l9xor~kS*`}ZIz7d`85MuCo= zwSlRP{eME#BjW#)0}P`x#3WaB$fN4ix?dvf{ z0jZUS1Rywem%zTr=*bBYH#;SF-}Fwpn9{ThX;EUa`fd4^!JKG zqVBIze>b2>XQXu#LpJ>cpO z+w0%z)ct$3s{hZx_^W*X6T@e5%li+vv@ZS5pejq=U9N4taDM8FB#H;J{FRi#5loUg z+1U3d!~xmdA>3q>O|R_K;Kyu7+myEev@*|2e^)ku=T^4b9Du@bbw;8eLeL%=UUD0X z)&hK*)_t9vw+8o0m&-)1ph5}Bf}X^Lj^im()12>!tS(}Z;#Tbmc#1+M_q~-zV?kak zlf~}3<;@z|m?rfNbEFnqRr|LgV!SWA^0zkrJt`f36y{=E%V6XY&f_AJ*FNR6G$((2 z(un{Hm&Nvflm+y>x;1D*Pr9TRRP|elDe>HxR0tG63a?pc6^SfEzS+|86uch0MvRe3 zB|zKMxi0#d7uU6oMLJ@+Xs|JHHRDtSz;=F5!#`1rYkPSQEB`({53I- z-<)K9Cr6|Iv}hBQrJ~oE;km-Q^tS~lQ7+j{fVeH=C7|Zn3V7l8Vj>D-2ZTvLCdoAN zuYG7m#h8gD!W(1&**xzKCz7$oyY^yprbVb#&-^1y*(eKTBAozB#gz^1HB24W-}@H- zy!$yU-iRBCkF?+qSLoT5M7~=GYKF4;b$Wjk$MMYA1wG(pb6?Iu&ra#Un&dJ8XGjvK zZRsG)Lc=aDRrR`pvXU47yXR3ws+gb;f9E_B!wDZXUd?KQH( z$vdkM(T>iQ0R2n#-6Iis{~ivyRi?^%?V zklh}e=JmNj(L*Lutd$5PuMv7n0U&`EUKFmQw`-g-TT5>4$0A5Pb*hJxdq3&b2|4E~ z2mys1QN^!mEWtlK>xymmUF0v|b6E12g}ZW#Gjm#!hHU;Ccz~!uw9OjEeCLt?+=xo^ z2MFhq7wugA&6qB}QUXPy$yAH6Rp%%uSyxbKXRDobbPESntk{i`u#kxJOjUks?YDJ& z6l1`Jb)>F?-@NMIf=`umm67}*?OMsHn`__RQ}C|5X_7-`wLXX5qNnecynTr@qu1~!m zbUw-odXWM!P;p5s!ov3e{X+GY2g`78K5n0Co86sbl!= zHsTUaINCgm*gc)awpKo$YP_9IuL79rNl^o?YKEK;->p5}eR{k1-y(vsXl_TIoD(pQV>81)#Dtu1t40s6%aq-Pc_1Rsw)$n(cC<6F zHly7@D@B_6)fTg7uhUvpj(#w*j5=}c{;qF* zJxQa$w-O-(34KYBddm=NyFt2T>INe!F5N+06l!1ZOVUnJHU8Ylla0=W zS1vwC1%9Zc5l~+*^&Um%RD+c2^Y}iwuoHR-x@wBOnQoKDbHormGCD?#nW9e{-|Tu< zZ{bJTII`jD7MfdM7>`-fFD=>R>zyQ^rVOP@iTcfe*#Mb$2ioJ!`6x`S z_T4<;K>EQUIxKcC$qpSMm=}x35Kpq#1MlyOA50FL1}NSmAtv*=W0s4@V0z(bBy#S8 zKDP2JK^4RcFlY}OOwjo8j}wNfHA zWN&X{Pit@FVEauG`iF)|y<(5`>UWF8mG{7zc*S{Onx;FSeIaAmdEoaunELAh5|nTc}ocPKcgNqY(2(N5=E6XXudtG`vnhI!Yh`TMbqBK5mYMFyzU4SLT_fGE3FK8n-(vA!D7s+U@SPmY ztX(amL{%#DC?Jmy5jC;9WxP)i=aHEPm`6C|3MHk;O@CTo=`AdO2_(ZvaT2`J>&!Qa z7t1@|y)Wpl3@#{I7gAYp2+WpBtck>3QZUQA?yhRcHeR_v20h8Lf*5JVi!x#BOUfWY zQknoY>`ihqVa-kuGqee{a8TQ^D+v6`ha%f~#SAGj_7Gs~Z=(DhoNqKnpO z41%+QdU?P4A#0FJhHA@Y^`%iz95?NF1rS`-UCO&fUlG)N0LVO!pq!};LsE?3<<*6c zf{4qLVi`;nSe!Q-9#h{VsUdbbA4RKY(D_(<&I!SLc4^Uu`lVLx+%^En;B;V5;V64Um~;(VuGbWom{rSbhgJTB41df9BFWN)~|cG zxUQE9Zak*`0~FEAO}sNk0Tgj**-uYwjhP__wh%>>d)_V7iSPWQ2LIAZe4OQrE<_B4 zQ$z{b>DUvti226u-RRj?UR5D3JY?NRzp3P{V*X*Gd5Ahw%YXXeXGteCD6`CJ*Tpe{ z1eZnoEJ-tDnW0%_LYLN~H{S6o87aM$+?(`6ejSSHMDg-+kGj z*d=@T?ghNS8J_(;DrSTa{m|zan>H8VEyrIWJH(vQMgJYL-hV3+t^EIJ>--Ou=)VNY zzjx3-lRe`7-$A=`cSs(bb-&B)7t#%i6$3K2Xpzb+fR!M|kqYkRfk4~gdiQyEXT|EX z&2MWcMHDxX!sa07N|$_w!s)YP66W_PF4R^}XO7~Jfk6U@U(t)K$NuBx$8PgMKV)m3 z|Fm|zEI?pfQnsWwkIdoi(lZ98k55z8Co||1b8{sg$CrK62fU^O$x4}p1iIKKjDva> zy7Tc`jnm^HzX9%q%fC2d+_>?=<=;>Ae_MThkM94?Nn>N+V4!FHzw8Un)pfp+Yeb($ z?tu(nRQnxqt@Rz;*)f4M!!^zH`QPyzRAG5Jg*APVlBc4hr89?Zc+WC0ew9?#76P1sO&yym@r#G1w0BrRv*B5$C(+LSwF!uMhrsEGjvO z$VYIQS}N`q)yvka&`{!ygecEln1yj`a%U=aGj{XQc~k*?aRdsy#NY`iCUH>`fe>gv z<;X{iWL0uxDad{k@M4ru0*k5AA7=Y8l!A}ZN6zQ*Pf1e99YJ;qc2EV(%xJK(iSO)& zi4Q{r)EMj3_1?WkQbO9C(#M4I13+o_p`;QnOHue%1BzB2uYWh#mBk%?+j7m8uuxRW zd>gXmA+RC`BX8~k{!ZX5HqO(Y^>r ziNbC(V}(2s>Z?Y*fT2=#?W$ZlFiaQZ^*cLdVm@!Egazcmgp`pv%PE+}`hv}5k(dbH zhM4;k#{oskJgF%m6q?!L$5OK|9NrLAke6tdS{wlS(}l%V9#S>Z;}d?=gQ!eS83>T@ z7=WZP1=nJKyx|4~C_L6y=$qUXbC^5GVSD#3HBAKc@>&S1~ zI{K-Onp2i?S*Oz9Qmi`m=XXLC1O0yNA+{lc$Xv;zx2jQ>Md zjD!}dEVYJ^&@7e@>ELQySd{PlXD8op_QNx(mWHF+F83IPR$a!0j}PoOkqk$y?hn7~ ziw|uuI8ul#j4rMwn@B+AXKo^y^ljEWh2S8<_gdg^P8fK*u+uO!ScFHT;X=PYN`jfa zp0{jV9%G?_(qM$acnL!NeOP|Z1(}x)auX8;MbUmAzf7Xz!ZKCqZVp3X$*t?GY@k5 zDDM@}(eEVG=7An9W)e-14w*RVEzuJ(Fksol9LpPT&f8v*8U`+_RI5LlZgZ5-I3r$aB8R~7Yb7=y+SLdkpmJ}2}RkXq~Y{fOq(%Zd%u4OPS;Vc zqIwB6e_la!56*FW>1a}`+(sl>k+3d+JqS!DaP7S~>P}uovXH|X=A%W0_5-q>p@t=m zjLfXw!$VrT=<1RNjr2kn1baTM_(@}e;Ev5rY&FdBP;CV+UcP!7+-$IaWmCOPi{Q1*ingZB5+Rm(??~^5jXSe3fLm+QoKI=%tvQi&2v6%IR#~O)xLau-LMnBY^B9ve zqXu)zVP^-7Ggwi`0$_Q#<(tUUPt%bfH@MAh_4g_?#)2KY?V649UjVFc@k$U|Ty0<` z^Dx{`FfyeZP^e{mENy!Xg9R_d?mqDaD*M6lZXE9ijc}n%sY7CO+HhE{Se0&|)v4KI z)mpIS#+RNlSV~fe&}O|d+H(s5+2hI01u-F4fatR3+^)ABB@ zCKkhs1DhMziGvzVTO0gME_Yf3BO#2-4MM_+Ig(Ow%Q{(O=b__in_(@_K9@G1VAmpV z;L*pM(N=4payv>x0fYTKbe_)a)>xXYJ8eh&YZ#l@sp-Oc`stB{r8wfAt{yFk6k`a} zDC(5Yoqy^{!Njw^S;Kwq^BzN9ygE~*d@n_NJxf)&2~ten;p%f_(8gv70c{`5DqB4# zJuAzlnXn+rg1Yv_eOjmPmJctx$jyVx|=51`Wef`j?)105iKRgQ;@ecs*s;Z zZV4 z>|a8kL^zT)E^i@6e)mArMACsONWxRr|l3fs^U*>0a2;WY|e zk|1$yOhFfNfLOmWk5=CkbJXu+PvoOFRRnt@9C?-DUgk$Xs@S!oI{XEm=)*;Y*~I^R zn^v}_Nw*t0U)Ze)Zv#AbfQXG?hqbQCjKd5> zGB~!|HoP+uzqTgQ6QLSrNKci{pY-ez)|1@&9QllS5LS*KPmY{kLiW_&4ahDk)%U=> zg{p`f)WCe}W93Y%LDQcLpjp?Cy4$N|UzQG0cn3FvTNx5FrmMs=Er(Zei} z^|W^+xrIJWQ+ZJ8!of;qVIf}@{JcL|X?L(PC=fYr(C8wIr006Uz@HJtC=eEBn1o<; zn9Exp9;(#I^_&$@yIJC`l>85sNn?c&`rXP7!SN5rIFIffzj!$TqIy0zwHF0=fpD>b zo;N=YzBXpK%kVC~>{NLKC*?jqcHRPBd)D$h0@8UlKdj-bN`@c+7R`oQ*RG<^`*t)G zw|LOm9|YFCbwoU#tGU;GTx<>D;5K2S9}B6);u$Go{=g0Op1wx7>Z`su!!d;h*arK> z2*L*1>fI$ONOVA@U!#ODnPP_KYZz~tpU8_@R8F|XAx7@hyFqJ{(v#%wOdqo6DA_Wk zxMjKuZjd{HAOhIPfwB0t;xLnevoSr4gPlsI^L`P|x8`_{6=K(kAJ_kZdRH(gLPWD~ zC~KW!ls*Uow&jo2!du&pbmmZY*%S`$7xtH}(xh35Wj&NXW_QVS7De2j)Ee@$ubg7d zLlR9$mZ0}z?s-?=vZ9P zx}peF`B`iLy+7ZIqRiAzXqpUKo_pgCGJR!2pE@?9F^}}yl`~Khra-bQsbC7?s2|H~ za4yf^+c9b|jcEH910SG7x>hpfo+X2;d$Vd2W!4LjeQ)j;$R<K`R(`9j?G5&W&&={eEOC_|<%$#I8ATwg#Gj6*8qDnWHn>=N|d^F)y;OVvnYq4^r9k_cC7|+|?2x5MeMtQDGzO>f4|f9E zVRHtL>(iCtBmwPwbDhV?k5#8Q&C>vpj=|C$nXCLs#6nRv(eundR*fRuJDXjm^@KzC z6%L!P)8)B<_C%N9)WPyVZ|U#R&|*|dZ#bu;Qk-pEm)TD`rd~`;mtfnN$qYS~>NH?d z+%ftj?6QjfHj_q-Xa{2o`G*;?L|#g5vtXFv)NzxICo%JOLf4iryZfgz$Sp$Mz;#>t zv|^YKt04(Mp{o|H(Xe1b;DJHj>A=J#gQUVDaXJAb*DDxqs~tT&^7C&yXiF|S*sn>=KX+Hfqfss(*i$A} z*%vGh-Eu^pv@$gvq>>tsJjk_B9=+Iu+8H|z{U5?HYGWAQy?GW(=CP;D=Jz2WRPSv>V(?baSe6WD>PGb&E#$G|n1LfXVO4B*E&>wxlE8NL0gwp3)}6#PMW9ykD6LR{@gUghr#Nl$>@-YH_=D)%Op{ z*usz;twXVDkzzIT1Wd`M6IhE9Nq%2OSLMV9>Kfh7qpfZS=wra6$`A+Y6xDdf?;v$0 z)M-L?Lrq>ZF|7!l;#O;eAD-OW{9o3oE~}`Apy1@FhqTu+QrqSO3YGKkvELn1)aCfu ziaw_WX~v zBMT;GnsyYk#FfTBm~S~Lx?w(xLwG_A)NObjy&0dgHD_ZQ{;nPm3_yx zl6i1`)(O6F(xp-klXmZq_UjfGJj~BHXK{S*6u-hUdPD@*eGkrcD-#qPy=_HT&_g|I zgd|NMsG@BgqOPQkPvWl1=k8kFy|6fBozYy+%R8BV(vk~UQoM9Ws3jgtF*Hmbr@kdp zH0RD!C+k;nH(1wrksf_magXzS9qX8TQZ8~Wq`YC@G2`ZSqmXi+{BnWfJCbnr;H5d( zt9giED)cXDY7PC>69HWuIr7P=v^16@NuJ>z|HT)PgXMx%eK6*0UmgzfNP_b0liV?x z*eimlVHr#eK#uu_t0QJqgiTADLcgmEL;7;2 zL%kyFjIgAV`T3v*GMj>&P%rOTeLarq{+nAU##M&tk;(tsYJq^ZT-ZI*E9~dDD|77HfgEo5-pOqAhY#Jw3?=Y zG?1|gzNc(;gc8k8mzO2fd5L=B%W_a6gMy;NX?^;S+Uf>rY}WK>MCFBrdx*wypoNIu z_%gb}ge3VPTWYSJF1B7IrS-!$xX@kIcJAOWVS}xK zBHkpj{-6fd2AIb8-e*L>D=EJz5rt%e!j)wcS)zqh+tyhvikKZd06o_TE`-a*!sNX= zKM16@1$=7yq5A#xBu-^jpsRX|?%}0TSMsi_u^9uyk~&2VEHE`r)1tfp^F)FXR9kkg zSjPo$m@TXxOxdNWJ}THHCaAj$KLXTP3rKn_I*lO*Y~vq4A$kLs!t@xB;wYg~sZU`> z$aGI1?kaMVE<@&u!W8d!U6!8O&hn@{kQiWqtCuNR4~2J;T^SV$YIctm#+1EJP|E00 z2KE`Wp;h(QSU?PGn{Bs@d$N*YKLGhSEfXf5Ma9g0r5yC&h<6-)(%@~1GoAPkYe3qB z9hv;nBShS7%2k)i;HemhBRRoRIy0Hk!8Cnk@5L`|OKDW8T)7w#$klo5GLQ=%N>+Zq zBE5XxLx!$dq0r`sYod2?L`>ca+;_b-eSJ0hK=b7T)FbMt@sx3izst@wd6;saEw!^a z??Xe6VKaBF3*>I{`s2x{7Y8hkC{_7*L0ZUqX%U*aJ$y8Q**-ckzZD&6Xx;_>3g#kI zorm-B0W;a^y~>ZMo_1`EIny8V)EJF0QU@xu9B8zjb!v>68UCr9iKY8Iq>k`?9=WZs zmEkhH_nqowtI4M`{lIcwHxoc=MRNr_%uzE|)QLFa4Y|!gt4em#tQgF59S$IR!4B6r z_1!b@n&tay&(ri>s=7-}@Gh{nHE@+wm@{B#1+RQig$c96td!$NO{RbP`EFGF28BI9 zN!4d}+!&04?kcy6<&9nop>1~^)~7BB{JLtZGQrl1$WtEYtqJmjuBo9|JArghCaO;h z{QC;CZdm_S@Pjlxc^zqdc=?A)aZxvQnyzg?4VdKuP*G&s<({tbV(sF#M0Eu~3NEDz zbQ@pFo1tLgY^Ie#rr&evqUSrMr+{DqJtRc~P(76LNzdMZe>3?YnTC<#3?@Q<1(`9w z?!zf$ptHfb(TSGd!ynx&GAipxr^9r8`bB2{Zjg>F6L1Xy-92pKLZWDhkO_r=iM~PM zn{RLTSYB)M=^s1@UAv4nfT;$8oXPWBz9ksQE-RK^p1_vFjZaZ@Wi44X>IFYcmb1^h(bjF`A2 zp9;2VZATl+@a<$a->V!!U`k-KxbsUbR%QWP-Eq2yo+~t_M6d-qF_fSEr}9NQL8iBQ ztX}b-q=F4Ge^kW|JR4i?cFc){Hx&hiz|3mP2X{uC7Hxo0OfV+I($0s&ne`NgL%hUs ze+<;E+#ig+yZ8|ig*U79zz&|X-*0^zRz={zbF?+)@J1vO zLI}GbiF$nvQGxOIw-xP@$gw$dU^#vi&dcd9tF#AYMQnNKW5m?R*1$PW8VS{95)oWL z0mZnBS>+jeDyrL}y)*xI*igq!%J9UhHF{*ZO-yw)XtdYsa1rQYuMYDC=TmE8i#ZRx zOt#o=k_Ju~%NWYZ?O2{!>ur>-BJ#cZw!?__*KL)=bNo79v7w9E5wLg;ZsM%ZO6}tqFo;Sx1CVd39;;5});_a*ovULS8IFSmFHWBK`W8hl`Y?D01aDo>%?-&wovY;QDW^ z5hH6uTbqB%qGZHP{R6feocc!Xe5->NxC1?IeuBRN>A1VUZN9QHpCQ>c?3tvZ!aapz-U+^G1C0SQJUQ*wEa*2J(sDn7+YNYTEbaLmO8h z;EP+>ChR9!VJhlUT?^@8rdpcl@iEbkd91aRRxh>WDLr(CJeD3={7uw;^BDiwBem-c zy7i6Y{gYQo?=n?7`8_P!--GvG51hb%Yv3I8tPS;TT>lfor!4i2@cmB~rE&JYO<>ioCdKK`|j&h=Q*I?)7Ro5w0HrUR?zH<>cP+_a%c z0$2(pee5dmT8c2yFM`Oduyt@)-YhMSNJvg>aG>_Rtgsr?uSgSPZh1?-IFRThE?tR( zmZF6`*_7SP)$GfoBcX~(BYAC0JbDFFyO{RYq;?hs#b{fF;=NADO52&0^(=wNnUreB zCUYEuUMoO1-&-b8dj=E5R!^4NH0YW)PIs~q?5>X{w#gs8PoVXqaT_Rpizz>S9+{@h;%BrNK`nmuxg}JOEqEJ4GuB|or zMhU=$vNAUh@DQ~Vg-YKmF1WAF8P-uxmXB0Jb%vV1Fb30*7X(NhqZ5WQM@r!?43kEP zFAhT^y6QYplWHRf=|FP_lOsocEUYbnxj?$H%;%j7QU5l*gxF|NW@N%wg%9yt^N*1l z@R7;+vh1l=U7>ubt*uQg!W&zNG?WR%_wVOqIDXBpi$a6;%i?}HRpHh?`%NoEV?A+{=zU|{jx+)V92I`D1%IF@D zHxz)P{Byf`4!t7VS6Cu`xUwSHl;jhGJ*dTbw~99GN~udIlc_|Y9x+C(#d!vb@R6NT1AKcUcqsoIg4s)uME48_Vd%su z_z!nr(b_w3?&mdXiUAC5$w6f~jHJ~>5lLamoC;cl_@h%2!)5Y#CG84$Pw&8V7{o)y z8?dlyJJ`<yxt`dFTaLN~Tn~PaTr_~%i8&{&=G_Tn^O=C}>8xG|Kd!7AXV79s`R=ti^ z?M~mb;djWFsj$x?+kc-&cbk1H{J@@bMf&$?^k9wz)#w)*buSn}R&CpkP>PPZ_E_M_ zw~DwDs@X)iyS*hc8zyxL>P#d9stupva&Vk@^&r=EPW9>4xBB=6XIEW{c}5!(uM9th z>qx2_xNONreuq^4X3M5!9j+YVnLSZ@fXJw<8Q8RmX+E3>?hpkE`Qreg7(;HDH7Bs8 z>D@~4L-^PCdGr)em_Iz#LBa9nxL@;HhX%o6xoqNL858i{JV^_S$4FcGuqiSLWv;F1l_mv#<`VWf!KX*6s>MbmRLmJ5LLu))mj7G$q*tH3ETiA_BpW$bPH2bR#UdX%s-GAO8w& zI7w7mp8bKahIZ4;{UNepI!PqfUNJaCg@{n(bf%Tr*90B7erfPf<~7roTFaOaLCo)@ zkMAon1*7SbG6TNh8&eVZBhl-i-0OU!hAOn}=P&6sx_&i%uouJ&wWYrF9~*7b1#5D9 z=&1-#zn195dlzv84RjV`Fp#2ny<1ya8sh-(FVW@VfG;gql2G#n7)Dv|dBJ;4(F8|J zd_l&f6XH$!VB@J_Rr%X3H_&e}0Vfl^z7M`^UOxkfqnI}es$QCHbXOQ0Tu)X=@i7jf zncl>kZny2q>p7}pd^8(D$O6sDNl*)0Xu5UoB_-%*#noW-GL z=1n{7N*tjpw80dbV$zy%!SI1;UJ)=brn%SH0XLvjpTHoTq((#%-#2F0K3Go{>9;I2 z;KOn?Df*pL(xvxtO_3dE&2Rc1VJGP_@M~}VquBYvvBio6>-1%9vZ=w_?Qm8`h3Q%oRmNz=K`X(KFy`= zZZbJ$V@Y4ubvdR_pU(HTR7}eMU zlz_)G$1@J*#LnViPT&)p?0l{(Z%_kvBlxQ#Q0?UA^=KQAJl$@`R}+K+60qV64v||7 z8DvtP2(kJ(h}^n2oN*9yc59M|K6<((!`evP5|9(nyOcY8FUk;snGW}H;C#o&*!C7Z z%;6Cam&LQ}^uc)v@#IN4{BIZvb0$5*y5CZm?4qnZ)VZlLmyPm)b=nl*hZDf-e;{@T zN@pm<`CbL-aY3J%SAVYH3E&pd-87P6iu)Dnb((x$HB3#dYD*2wpxXDoM1)4EOyqJ% zkxtsJ5iQ-Fma|YZGb`^2RkrhKbp$TN|1PJJ z&K}*^CX|@J5<4tgu$kz6QfI8xcxqfhV47pZM@aH%oWzQMC=p#;+G4NL9oXVR{|*|L zWlEc{h~z&5I*VJyXj|!?9~Ds7$sj&n105*WK4tzm-)}Wfd5mgusm}z!wnRV<`=|AW zFFp`^O5S>OlsJ=E!}ioTjVw(mzwGIgE;>@zm_N%uCqjQa`c400L2%|Ev=|Og7?(GE z;?M0BTn2)0JoWhMY-KxZs#5>ZE?+reshWB~&1rriz>H8tdEK_zkR13-3k z*bW}{2zs4FDrFZIZ|IwU^Bw=vB&Fj_{bG%PTUNezsH7hag6(X7%-!H4-EoSE##4!2 z5gVKHh;NWtF%BP2cFyuP^(^qa3iYXveFVBy*wy?hgYyO+YCQC8SOE7m{*0RlH5gPq z1{0A-ak1bKRI)ME%OeAlOC|(jiqmIjmn8Xc!fTfwT33}qmiI<1@IwKJQ(#ouUO%I| zWmWoEJ}8vtISv?op^%h5YIAa1tYr-#SuSW)OrT2q@)(pAj@V?M3ftQQTg8*>RDMEI zGJx?zXbg;CTg+;y99MAt36q)&*_gsrI89|RqXIaynYa9-A=O3!)QVI#l{a{`ozD1C zhP=#iHH+Ue=S*=;L`=W&XOI#Worh`Ftit?wS3k92Vnq;+TM%6v?Uu1dXIKSD4FHAp zEz8vvR1)Mzv@8=jF!$j6|3X_k4k%NMze}E5qQA@DW&f@0{XbZXv<9X|1{VMP{JtWH zknf4gr-SC&tEy)GlVhIVw;AJpLVDk-4gsz6H+};5Rc~4>smSKXtWo z3AKsZ!0V0L4r1D>ZPp8%*KB>vncz{$t_;;mZk__urhXy0P`kACM}Ga9pFv@`OwmR% zJedz&KPuczuT)ITO;~I8GEMHO}!OO7GD;SKGGE;t)E#!h-WQ*ufoxS=tB~*Q?e0m=)svG7%Z)x)o(1{q_*Sj-E~N@WQ%dwY za2jqBexs}sf9An4+~K$Jz;hxBltj1+4F>exrw zqp!qv$Y7nHVNzuF_=|K8Kb-1)M~o6;o<_#qeR^Y|RNQ>u9N$b1@uz(^4uMa;nkyRW zBU)_Gne(&x97g3(t&>t+uI1_;e|rVGRlQyy!sfqVblw0UNsaF!E&uzU|GF$Q{(m^J zhBgLHRz}v2I(klyrr+OY24;?KI{$Ohzo)xEk;CSjWSupkV|Ohp8%zkh(aJ>4?mRr3 z8XYdQOrUkEx_Zdj8*SD-<=|K1WVP;UZqFwnP3 zyBHJ4OIk6Pu$(%xi&6bLaL5+{M<_;@9er4(ndWC^^fRbTK@4M0Pz5uW1WonX8Ypf& z7dmuKTEmSk65C!5>9_3a?MxyE$JPy_L;;X^?_gZt+#ebbY3Z%LF{tHnJQzcqlpwW&SQn1GagC5d_&&VvE78ltsQoGMSsO_d2jc#~PvdgpuD2>SMd>LE9 z4)eq&OQ#M0IxuG=pYMA8pZRSSHNsKycc_^C?bXLW#MS>VxJj$I?`{O9=ur`HENoRZK6nTt= zLVQ>zat-S`S7fpsNB3ej_+a~~T8!0!6@^`Bj>A(Z)Ea3ueXRGGd-msr@528e?j6G` zQM+xyq++weif!AeI2GHe*tRRSZQEwWte|4sw$-b?efQp{&$(Z9->3IE{co*2`L*6L zImVomtq86$9Wce0%95j9FAOk+WD}aPV%DEhcF3| zEWS2)YaXqr>?QF!{8H#7>{SFW1ts&MohUHHmq(}j&WAx3a0PV1O&W%z8v0`*Ev9Z< zaz`y{0ds3KkVL^p@II5J$$5K~J@Lz!l?*QJPZYexwLfW4mF=!&A zDA|U-da(e)I?Y9H+r0S(K~_!OqKIStb@4n|&vbkoE2EEgO1$l0#`lYKCyr|kjvWN62+T$h$(2xe zc^s6d=-MshAPP+jD6NsodbBq7a5@`BId_cl^X~*<%%S8ePY~tSA4BVh!j!n=ItmYH z*0h+VyY}MFFV9hSuV_b@Y+)>JN!>LOTESSmLH};wF+SX*zXETp!sA4POHNl9w{2KB!w^=52)*XPwjS@ zdnAr#A~}A^`L+zQr=n4n6J()a9eU5~AU|x&kTTbxQl2D=S<7cHs1t+d>v|ZO$+O+_ zs3~xih$}`)YsUv7C~p=FHB!J?D2WPO`K4A6q6#4b|M&}%R8 zuR;?@s1^wV#mfd!eRQJj4NQzIft%E|4X0G{f|^Dznb^+NV%f_L4e7LLOy4EipQ28l z9oGAuxyQ;d1p619wl0~^$r~fs!a`kvk6nM%4MDaLAmACnOeEoM6>T3uq{;$L&FP_Y z3++H^68vPx^$2xpN_rZ*73Ayp_j=1ZX?2gjud_fW_X9;*B`2^RepZ=QDvds3_BsZeqBLkGG{TtD2ozj?`KAc7Xe8gNqO` zWEE?IMKsP4TdUcJGTQuJc{U(osKu(819d|KI>|CNeyYxy*fCnH8><4Eed~XjYEvU~ zegeQ$34a?SO#dff@^^&jw)$6uP-zxRP#tCnTBF{LWo;>_kT2m8lI!bPkem;sffZM# zdUppqMyyKJi2DIO0o%@YK6WBqT5*yik1eYqG1hF4OC=2jQkKrzh~DW}7mhp^F+@&M z&ej}qEYc!(8oL8pb zd$#-SD_2a&2|8h|tnmAQIbf{(-Lv>q#ovOD!*n*akVBKTAr^Wetr#?^wK{5>o zId~z&E|rv7NN+mdP+n5q?F_+mHW^0W(j7=H%olB`Wp44(G1Xppmr(OL<|<~~4Thnf z6VwT*xqZQ2(v-wwwcBWUZStj10sjHwrth8^t&o8@+Z5auZ0V?bEm40M-^7)bNLVmr zd$6<%D$PP=R(E&TC8#-pJwrQRLW@YorOE_HK8C33?#{L|x#rya;=vFUnprE^h`p>!PXcI(zt$9OpY;ED{1#-oM#UXIX_zJ-B5Cp!^m@|loSI&& zK<;8)Rr{IVOMHnZ_b1?-*Di6xhcJV00Dp})Um7|$rWFGhXUjSXd!w| z^|1&p&zEOst1>m-#t zbEYEz@?H1ayFv}tGAsP%wz?)Te^^bqKK#*iqh?)q@znu61z9oyhsR(W1KFj$6T>~}>@B6%ZzTmt(l%OLmAfk|vuIu}Hmai0 zOM+dxowxF3oS;7r{HNy3D>i-)EDE8b)4r zaznbr_o=M8F2uA$9XH`qL_E-$#XXXgeCX`pq}a$@KV}TRoj4#`*e+a9Aq&$n3Jc%C zLvQ3pB^1ReRN0gG1mz=5N+7LV{Z%Pujz}&OnyneMt!?J|HsN#G%@*ftfNuhm!b)3XhIG290Y9p|DW#j1MV`W{MZJG+^mM*K}mk)Nc9CMml2=kx&cce$v zuxA{AS&!GlcdKxrJEA^Gx(Q8wWfuy$u322O-ZX-Xk?Hrp#5y%14gaFi%O9kZm0u@% z=3v_Vz4{iHo8_uu3bJwcLu2pIioVMFatb%)3JT6uI2vG+uVp-+!O}iQm5TapyL-y& zT83{;Gv8nR3;J!4Q&Rx^w|)zM<9`tTrUp8H)usB@mNsAQOzi=etp2PN^5Z+L01b=* z<4X#7Ea+j`zMQmgwBoYal0gm!$+9zxdX_PiuBB8MUibXor|gLZeYh6e4lifV8E2}H zElB2|DLE_jg;zbQzCNyR>T=4YHOw43L`s3ERh?&Zd*cPzZ|77p5f1VuXa3SC*I61b z8o5MkCbnS>@!oM$5|&UE6M_TmL3mM7B8ePkZuG7M<#r-`GYuP1Fk$d49Ej0Q;`kCH zvT?DYrSQ?EAF2-6YYT`Mn5-xF+&g8lGN3hXMZdEp>Q1VMP4v)tr%y_*-xg)MGfh}} z8Q2LYqbokro!Dd?eOFvYr6InxhGR|T_%;iJbGRd(?0WV&JmWO0IgOf}2D)ddbzn80 zuaUALJrCQzI(ejof=1qy#Uo{86_2xUJ+ziAo^(R4$Txm+Bl?qUpziuWgjMcoJoA8` z!IF|oj*v}Zy9qj(R~icnM~IA$WqVpF8oZW-w405!oP~VdH2##)RV;z53|;gi_e(a; zCt>%U&Bc9Fx*;8-PZ}qYE(;;sj#E~>QWr$RezOe$X{+HiJa2Pq-t$3wH!C!+kmlE9 zkOUYL5kM6ztmzHK?t2Z+bI5Dn=ti-kk-46+(!;X}DBUa7wN917DZ>sv`Hv_Hw7$T_Q)$%EZuhSZz z8FFE8$OnRHl~ns5RaLd<2rMjR9iP%^PE(SOpU$*sDlUA}mh6f4?{b_jsC*F|!Ao$6 zVV`8^XcV!#Lqkv(e=mkUwF^;E32mc+i}7 zlt8kJrrJ*_VCeuzZvthja0bD3CDm1*C`;WL4LGJG^m7mEf=A)Z3jqbL~>W%V{M1}-{ z$8~4EhJS)AW5F2qtDPWmvv=4KZ(K)aaMFvN3Pn&G>ZzJ%x#WUe#B=Gf*2{yk;;KQ$ z@O;0$L%oUj=#~mNV_A7$w1XJwS1-q~(XGtEI#KNqP5M?XV%Nk&3s2?9Er3Lf(7ux< z(*1zq?A5Pvr{}5-)2e9-cil};0+Fd{0){Lp% zT4oaC%gX08Gx+%3IqfTv3fNZhGZm%1{<8<$y(81fr7=O#YW*?g5rX{q&iCND*DvNy z^%eoA;8Vxbp`mLROkWMxMhdf>aL(SWK*#GDC9WIs0;&uz-!sjfw9vNyp0AgM`#wYg zaF=qwh44SwO0h6?GPDBR6aLFVhDUeG1koe*@2~0Dex>J!JRzoS1gl z`QBzqUZ@mkU39m0ysy2+8ETjg+5bfk_<++RHYvva~Xl;lG-OZVT#t(dkOE8 zIZV1wR$WKEz0$87$rViUGVJZk2&?F1XCywMz)tMZl)}6&CECNp1~K8^O5R_9+^*Gi zhikt3Dm{>_rjt_9(a+xQBYta2dB>e5qyIdP9q|%~&#gHEecV2Hif)$#gCt(`@pl7f%w6V5xpwk1KlQcK5 zcDDK@ri>SeO5;WBpLkH9C@1%)4c24|I{k)UR*lRWw0HHVUQ+NM!w!rTO)l`%FJHc zyevMWQR+O}e(!)(5rJjqha#yRbmWcwwaMQD#OIc7b(nDW^X;^j^gQ*WpqmLCJ%Ykn z-#~lxX6|g)Jzi9VMo{vlRFS#U_W>_lq2*{n!z?tO%oW6-E+|bMzd9i;qT2kF%ZDo> za)0n7#9AbXN!a#|gERzZSnUx-*ZzA{?9Go7-^rH!s@ic5_D*!~bMHRl7?+v}uQX4J zbB+tujo}|hof&fA8n;L{q8P~{BQzP4CHfblDkwNg zsolt!tq=n4_8G%8zpI=DZ_g{oqiZ*Wce8Vf&tBep#wQv(J#9GNBiz-fl5 zmDL0LI7X1))k3qCL+5%AcS9r9o;VdTI#lf`xcox-pV!)p9y4}`R{8Sp&N|}Q>b;Lr z9K0+4?%2`Ji1__lmhgOL)vbjc6`5od9Ga!wIjpQg;zh0mM_^(B8j)5FQ~{j zP1I>;Nk`T;561(Inqp3u@4?0{gRM~Va=kaAXom<&FN~k-jz59sM%#z)tE|1>;}%o; zCK=s<1wULV-&kb8Bu$OMK6u5{==&Vkz?|prk=U#iG`36=-{$XLmUHn{A--mBx{u@` z+vcFH(#7OE2&z1s6cO_U*l`$F&W9U69Ftgi2{)-liBg#Ta>;6<6zPbtWqidKNBNit z&_VksjPZ)w^1*_byA=_-ex1(m!^(oa%}XI}XIn+&Pca}j^ys=ZYfIUslFe{a-SGqB z<^9)NFQ%$wn4ci3bvy82aeG{A}; z-XD4iYR}j^*!4w9#S&+*>Y}8v%2D7a1JgTh+Vgl9VhiIDAu!T&cK9huKtshfUWi84 zVD_5?N#bt!z*s8C<6Io3n8k&n?0=9vBUZyD*YSAU(0E&H`ywRn@?kV@D7~+^cy;V& z*gn>Tv^UZ5-Cfz(yBtBIs$-P|JT0qidiT^pId^;hwU+N^GXlu39=+;s1IvE{+UyJg zNjQK{1hirPI9Z>rC>6CtkH}-})lVKYQi8Z_pRf4@a#x+Oq|e3x(ww$16^P_Dg84GHF3nVyl>lZQZ&xx$Ay_Jqs0D8Riu=I?2R~m^l{iV(AF0KXex5&_yPmHuPB!`~P5xeI`2L zwVI4(F}WYNeQR18A3Qj$Hp;QQgpU-Crwumzw0pR(gp?B4zH_-w32K`&d zW%$oKuKzof{`6iuaZ=VZ^ngXVcUAO>(a7}@@GFG3zV)KeOUgEyDF(4D4@5Xo=%r@w zTz0V{A&E|5BM}O7nFyWdOm!Wl-o@~sg6A>p^Q@l211X>?&mwJ=FSzY->BDpKPl%vX zm2v4zi;oVJL+&Ue+t2a=Kw`1`=}nWfTWB9nv+w47%~4Iyd4(*oy2AU~O5|+Q!XU-;{ahh7A>&ytI4T_~qI z4O|nnLv>-dNo~CjT*YqoBwr-eKPEoPyM)7;IHcr}jhyN2q0mC#GwEz%d{uV_4q(1_Tw*a&ZI{zi# z%~u?YOruBS-7dn|l}#z`4qBtUjnyv0U6SBgHbSHBGCC!&K;2zc;qRe^B6zk3-g(@6 z{t9kZ0+T{;@X4_|xx{QAiaks@C8u33#3^AY`Ytm3fHrN^Qcyj3l`V!7r@mg03vdb! zy3Dx&%v51F(|>Rf93*7T_hNxVwI-Q?V1rJa@hNFBrytSFDcDkf1eK@w43|HXo*tNC zX});|XNWCzqbt|0a5S>x$=U*)yk1m%funkX)}$tA;K+~`x7SVJu!}Hi#sx;lDi3}z z?)W|MimzBpDMyL~)0hAsEc;Wp%tXeiOwi^%Ym2%a@gBu5Rsi8hQYQx*T75w^XbqNwnIK3oEz!aRfdEtu z*L}8uL{R*IatSA*PH=ZIVV(NhZ;SgIj5A zXxQD|?)#E=ycb_eo5hW5{^Xou0Gw0XcL)tC<}>S;^KtMu)vdN8!Y{=4C0;PGuFomU zG@srKhp6h*To{(?qP<$;+0i%Q(275nrg%eJUJ5@}D`gCoz0xH=N*D|CTU$^e+oi_E zdPcAdQ7^{Ge5F!I0LdyV06&}<9=AI@ZQiR(ZuelnLM)?5+hr_1#@wP_HP;5AoCee8 z<&KQnZr)qn*+lsc3uR&de)%@df_Et94M<%6h(9$4v740u00I3LK(_y2{IPemv9U1x zC5rlL=lXYd@xRhb<$$@CZFX25jZWX8uU{cdJr*>4G+X;|?|3$?+3@3Igu2@PC-e<48sW1f9RzA@d-w; zZwx>*jd*(Xvbk;az1xa+>ze+|C4~8OQo@&S3556^DL>ssG8LZWNmqk00pG>An`nuW zEGl{Ka<^#KjD_jeyAB;~v`x>9&gmxKS!bAA7$oG`5tc6Kq4qc%A~p|sdAIwiN=QYp z`^SIen(#WH8lJ*i@6@ugV#9ne8M}U zQ*oH%32?V2v{0b1Rm!CIZE31Q8Nyc*>DE~j@XYI!{Lgfgk;QyzBP&_e9xAEI45ri$ zG+%x)Tbwb+U8{100f!?{;o7x8$uhu*~rqSx5IN3BD(Y+7t1n&sxZ2 z)v9bzh{yCv(DdGbE17`z&ol~eY`B4v9yGVL7uvxxws<|pQxXPx8M1ntIpxqOw%K{% zZf-axZhoc{w2P5z=uLw?t+J4nE__KYsd;l*M(48bM=VtCUK_U!5o-3@y5u5Qgo$b^ zvkvMKOQQ)@Id|++iv!1ZEvhYl3H@HoV@?cz0qFzpgqIk|TTu*w1El(2lvA-MAM<(P zEEX>Zhd9VsWsCbtCDyl!>>u^I0?*7EuECQooXyik-Oxa8U55Jh&3M|dWp!HT>5(vK zpU|zY>xiCl{^Dh`zAS279Ets zl}9b#so-90b!%Yp3xK1#wH+0RG(dX+4AcNcOvdVErS<02ttX(nx?|*78gRXd(A=vK z`BEp!XIop*y%T5gd3Xg#XZA_?BPK?i(xBQ7D^JjJ+^RMwLxKNGY;!x%vHEDrEY9ec z2VK+asWHH<3a)ncvq1MND3V$z6TCV$P>K;&nBwliC6V^r2Uz+!w zRl#%Kpk6;&>1;%6mBgZ4?IgvZ4=9b_D~YQ;TaPKIWs3PUMwWi6&>ah<1{@8}l$>t{ zt>c-wd>hG)F|);a_qY`sRnSE`idHprmqg0X22fF(V3FKVo1!B(RBTSe%iJaoNTdK*189?8S=5F=)0Gew3CSZ@Q7Ic6_v-b783nl6q3ne+&x}JZBt?<;^*uKgshSRC|oVKN@!42MIT5_H-IIPI&!bItmBgfwo#P0E_9 zz;*QIYvWJv+G{HAMR5|YHQcV?bLnN-yG}r)_#UUgcR2PBP^7Lam&5BIs~OCBYtLY}F=jU{^UW{w>|3dWq1@bL5EnHa zL;QotAPf1%0=U)*$Y&Fr&$*mB-%5X`iC@fY5_TG6!S+(uYNei47hZEWtAv4azKNCu zw9bw)?a(Y~uJS^84A|zaB2w~x$Bfz*Np!(%Uo_eSJ7JYKjzPNO$6c#X84sE!9=!H! z6la>(Koj=e1KW{IaOcnE7e)neW`=y8suG#PiH#Sat?k;PB??p81`wC5&zLO`=i1xZ z&S~DxntUjw1A&wKPaa@xH>-(B#?Gf(2#c5J7h8mt?~AuosXrTksjobw*#^UIEoa|L ztBh9RFaO%|TRWE~U;+R_+;39`vwzSX{`;r@(*@ScOPQ~5Bk}@xp{flq8jJGJ)`6Px zb_Y48Vv_EVIVKe~6_qrWqInjH{QzR9jX_)epr9o=#yEdpKQqI$u@GeP=BEXR^ENF$ zRDTNSI_R-Sk$2w)iE%BCxZSwHez8niDOBE>5B#_E@vJghP2slLiSv3mDU0y=NtZS# zI;>4&U$Isn6aR36$IxrmN0F89$+m1=NozSBY+^wOXxto}?GxK|Tl~b$7^ACjn;ArFo00jV`siR;y4>X8@Hr;7DUPhLpk(y_k!J@!op3 zVYo;Ot}+s{A$?2-@a}3e6Dw!B?@a~mwP|B2*P&>4bO9xjL~H#1ECxn!9PP8Bel=`< zlvi&bM?DNLD3LkWOTyFl>(R21PmyQ0$S1y)*(>?#lomidPM8+l+I3E=j~k6VU6SiB zrC+Pj^|odb&?0|NL>p=*0Q}X|EwwO2=H>69!Mj6B-&_E4&VQ@F*Zv2Q<7RDb`CqHl zbcKoYI6#kfx^pYQk0MOR?ZRlwP8YH`WWN>z5J>T-lQIG9L$bOr8E!Zh&KLYodhr=U+Q@J%W5 zWdKDg%&r$?)Vez?u?SR;zG8H3I0J$1x#n~Cr=NO|=lWjDX@&dau}(F-HB4_0xU7u8qZ~>0DQsT{5=#HS~ISfgmf9FABLK@E+n`FiKM?(@e1D*#C=qeL(W?6@qWoW!RYCyXv;=!RG9_g=MbJSh(qPajM>ylqMT z!ZY+nclH-|oO8_|3;ng}vm5g?A=gNnK%H{MXip{!ThbhzN*!VmuXHK964w5rMN(wB zcI9!gmLDR4C7pKaW=Akc>WWNw{2un}N9{TmDfWNRyFNPX@G90Z{b-Q77Ty|N=CB4z zIOCzq$}HM99dYG5zW`ewAirx~T)7O9zWJNQt%vwHQZ%*GKiSBvZGwFJ|V0wbW=x7YZGE<^36A$C#1?v{dvSIhV17gktt#-5~I@a6D zvuwXuF@!ZiFX5IgaQ8he-tJ9SV7twyUy#0vFbbBKx$+3+sI4v|u7P@wqF>*pDuBKn z3UzqsY%mtM7IwHS?coYLORcS*PHBN1w}FvqstDH*eF8~>7<1%CfoIEtoWK9EJC=Uk zQUS@v^u{LdKzWP#bpQit$_bb+x#XSrKXhl*+~0boJzX=Ly86r+QTpkZ zl|W;uxtKsgR#{>XG!7Nr#C&bdfjV53KA5g*_(j-UoY0{0=6+`#cw#ct@U-g^?-_HH zN3$u)8)sVs?3@Fd|MmXX;D+xCF*A`z&GZell2v_;B6a>V`C2vqRjiD?ZV z>Y0(|<_dJ}uUInL;V+??6V{YCdNP=7lT37T2Ki2)7FLf?#nkq8^W%cr3^V;2F7vUk z!{zco^hP_EESo`eM?0R}0knD(L2CPQ+amsti^$K2UG$DaSl4TM;Caqsu95L&hf&ac ziu&qmVi7p5#hHSMXFsW){<>5+Iv_K23dl~hQGZwJ|H^3op|P(&oz0)IX-8$e2%Q&sECG`E(K+NN3yhz!qM9CQm zp$qqQgfU=g1lp!XJ!vGqcajk0a$$cW%r5`(2mh~`a*a;!vFX6eAIb!Qmm9lMOQl*q zebsI@&vnw2Q+aRKxOQf2b3FI5XNn{d8BGW_lziJbTWxdsd_nVIi(ZM3wHM{8KBLH z!J)=-zT&0P5Ndm{Z#!V(!mkj?{lYEfy{AcO+a)UId^Wlo4oafWyLr7Pm0wlM1S5=m z85?|kAiD$hUnm9O%uo;C0h35Hl88#sMj8p08os*PsQ!jxa&MpBTn9JEL+HrqhH-Z? zyu9F|ixshZQ2P~6j}#c#kL@SvKS%!XrQhXa;Ssce{vfrePGU+1(-w3M9a4cvs-J(c zC#0Ok z#2wdh?dIOk`A~zp4a8m2kI?tuQ)q3j=niTEc>o4uR&bP#Vxc2-rk9!*!>|fPiv4uD5p-Ym6B<)~ zsNN@&R#cyO2Yk1Bm6(qzbupHSJR#cxjs?hazjeG`aarDVoG7@LIUQVO+axysP00>P zDwaJs&V0ku9rSs4Y+B~_K#i}V|4XN-5m6NnxzcA{#hN?fAG=;Zua^|ofBGMUhklMm z3NWGlc7IyfT2gvsv>=cwx$_eLOQ888B|^x zh`&C~qtsRrc;m>4<5wgOK7BcwH5hidL!xqp-+L(FGW`k>at}F5g;ONL0h*ljB}6OK zb*{Zsj;_+E?uXdm?f$%VF;z5LYCUkQk-C}B{pKqoG5RI<7vneX7U^wS1zD7f^ez=A z$16Vu8uC1_ku-$C^R~ygeR+5JHSO4V+6bMk-Fw-6k))TuXLvqa3{Y7B44x9{cLx8@ zh?$H`EgTH(=>8Ja{_*|S956u4G)<2f0AO8Rj(|DH_0AKTtEnQ>quKH69WxGD>lb1M zCreAO*5zu&tLv0+;_q* z4KZ754Xh8-9c96nlNOvq1=tH)-o}H_HIQq-Ucdai}K0k?Q(&m<_2b6KWz*?c|A zdU+YaTd`=JRzla{K!d8YbmsW;8qpQee}q!Xe?teJ4{pLk#^V)agIkm+SeFzk)B9dz zi5uFD>Y^Lmf?;(JPOls?b?9X68U!>B>xy>sd4mAwgCteH zn8-PnDsvpF7L*2_A=%$l$?`Sz{IBX&w_4&A#42I(}n*uh@c?}8%wB!?~_U06VZ(iIxy^lh#L?`j95=b3@ zN>9s)Xum-*7gWTXUY)OD0e_jH3BVJS8Dak56EYC!jj2}V`F5duk}`QnCkK%65C8LT!Xgx+`wv^8>s#k>{EWjs zH9k(x9V?FH0ZlYkCi6KM5Cf(uK-FRlZAwoA9LK~FRI!I!d&W8kHds&w_Ntw3ZPJgQ zumkJj*S7B*O6<1@(-*?WT^53=CcxjkmCXh_j8$StQ9}ZJY z4^?xH?sB|ODa=p%o(ntv3zfNc|qOYH256J zpDXz?RJd8dF|m;TcMFwp{w25vfG1bT-?^Uu!vk;srm*?*%%BcnA*GS2@vqWGfhP(u zRhrSSBjCcmAP~I_;&OP(1`5Wh{nD)oSfeC41qUzi=DrzKAA78sB;6>+bHsC1LsP17 zDLr(PM9r{ zyP_1TLazhEaVpj-0%rR$<8Hmb%`!J2ea!{7@^z6iL*rHKPFFipF~e!54pmdQ2ukC& zh7H$m7}pBsrPHCYB&6n~lTnhkrW52X_@^jgmm&g(w1M_vt>$+<%6L#12M4u6prCOj z1gd;YfXYdyo8EK~J`0Ub(;B74S~+U-OK2yY=HXj@| zW*;Alyp^DAqzqQ0cdQz?8(|i#Zy@I8PV9R&xd%Z6ETR!F`p0fN59kT+VXWv*I41jx zP7DM-)4;=%9;~2>36UnDK=ffnD8Ioq%rf0kq$z#QP@%ABqh&;TyImnjMy)x_jt z;l91KPu-Z10&A6nc-$uM8t}d}s$@^%tBi(1hQT%$N~*D2hUEr$C!nkTN7ICex=uU` z01!y9-vQ*G(J%iefc)iY;$(hBrv8b$qF;I-#R3ScggeAq0qmOByqN$k0)qgf*i=%d zIVZII(sdk;5(x?P$zD4kTRUvNiRZDpqT#vaa?sq(958(bG{?Mp5?m3~9razo#BDke z@lYfhvtu-v0cbV(a3q?9XLQv9N+vH4p7C&CONu)NTJ;uE*l?+CW(lm|cxs`?!jDSu zsm@e7R*D+C6x{WKpXv!DN`G?k28!x5bX4f3^Y|1sHp&V%tvm-iybv|9 zuPTO`ant`XCf80$R&H50C!jt+w7Xk+Nb+-AD1^)UAf#z^MIy#2gUGRf^ylNRUfv@b zS)g{q3A+BgkZqVdTX4BtXIYwW)xFY}?NU1_W041Eju6vd(xjAKi%xlx)-T0Kc=Q{h z3-%Gyt++|A=U6K;qH!EsY*ryYlX0bLULU*Grw%X;sCf)NI1UNouGcV!t1@Oy4iCx@ zwQAC!HPxU59KN0-oQT$kB^~pwXGQdUYzr}_4ncNW$L5YeTJ4)`v+rJ}4}D}dn3U>t zPHT@czO-|#4Bwj;PA2^k_RP+Cc8@+rQ5AsdsC;|(R`^2KSVWTo4q|2lb8K!JVwP{Sc*mZYfK%+@hLWxHte zb+_>7jc9Ihl@;b0#EI=`$)n`x$NVU49|e0m*F=;o^>@cG$$P9&OrwXtR6LhWhZ*~T zU;Y8`d;fby{Xa8n^Phg}zlM4NAMC#B|B{vJ{I3W2m;T~^U1#qVWNn|z5k>&_ICO(K zDIl)}c7ycwM;?qc3v(ErBo*0s*N)D;6`(NZD1onG{KR|3GdBOtnCrVRzLit>Zoh!3 zZ?Z5-d4eBhEM6zEpdsltBZFqB zVh1tSJI$nQ?eM+7TTfv)2~@d~G8*|InvTg>h_@%f7}H2X`>qiL76KNW1Y?(giw?dA z0b)A4F5o^vS|mifzo}RfW;a!sk+G1u@g!WToy;vm?{dfVHlN7wu29=MY19d`@7THF zcl2U{hR(#F6RU&K9G_X!m*QW{^k0ZX-5F6|Y>dVB?cB2@1v@q|zh`1~S|4|2qEaM- zfulhYk3#mND@ja=7R##UGbjjulef8N(4Ff%mm2J++=GmaZQE?Z6$Vp}dRN@H$7QwJ z*Zj!w)i!&3r$)dtjQu7Pkp&$A;uv!Seq>hkc>5^=L9L+<#@)VRNvgfE0_W$2dwL4n zl($^$Nl>kJW^mA0X#xbyhRO2Ejm-SCk!1$0_ttgu#W=H_U>YNG)?fkXay%opryzI0 zBDrXpp9p1xzf2x$dnWf{r@fn*@BAG9hM>cdx)Rnm)Sq!xy^+G{k?pGv8}SQysEiLyermTO+uNGoR2>@1R;$&sZ79_8Bh^^7p9o$1|!2)MSg9`p_d}_5BI8 zosokpP0_MV^UI7r6}~8%6_~Zs^Rv>LjzfpDEiW{PXPhb}nd!7F#0iI@isrFh$SX+>Bdc@p=9g<6bP>ZJDoN7)XpTVjiIdY+hkVC zcS*F#Roi%s!~CByD4Z<}KZbfNq2Tir4IvY!aSKdKNU-UIwGHSQYexmPNvLg!Wy6z9 z0)z~23T(pSGyP<2IxVM(Pe#CRDGuavr&USx-)HbdGxwpMABM z$tu7`$!Gg=)^VtSf@Nt|F*P4biJ)m?1eWV#$$t0a&&ZZ$Db0uITuY<0aI)s&($d^n zCJ!dheB^u5iXGp?kMSbaCDGND>Ru+;n`t0JL<8UTOLc5Mmb|$fH@F`6nlQ4NVAR&1c|((YjcqD)gq zbF2l_V*Nwk%j5Z+?|l^^$b%0P$X7^gjYXX|92WOx#Y(R8vZ?O^JowP*z8R*Uw=ctw z*%h7>>8%sax)H^k>7iMt>3i>HNezm*5n4Kowoe7m?5*zQ**Kle>bjEVYQJaxqgwFe z&dbT`7cthPsxVu2peRd)JXCWOaYX~o)oJu?hg~(GVT@pQ`C^3Z<>}f z3F1>1hxWew{uKxbO!q%*CgN}Q0V7POxtXeLWP=7eV4}3sTQ?$DpN_VPC>Vk6 zz1FBl1+9w=_Y2LxlOmL3K|e`S)Pk}I-C5cqT;P1^;$4ghQovf8%)4yk*Ff26Z)V94 zSXqG*FP`TfND6g@LLTrRRslpmE|Wca8U6CBPli3m4Ze%6Ec4^2k{S_)4kV zq;sEcoo=pGnNRTV`qI;xu(|54m7){9Y%hvh;+$^}OvAbJ#OLZ4WvFLlOP#-V?IYnJ zneX^Fz7z9CnNT#+8gkP%@dYf}(6w!0mCPXX!rgn7s5g2+xpA{bex%(H-#U=VGv}SB) zmx^FC{eYuc1%wqh4xk*YD|51fDtpukE%;79qN;>j)H2ahHru}pnleo|BLf&6HGjlAd(~ zem%lrX#g8FKQ9m4bS6zJv)54XO2G%erEYRRF4a`v89R;RRNc?);jV&CRsmBsQ0spK z^ril=Bl?%((rkboxd8b0e{VYQzn0Z-f4D#suo}NWoDqQcz*JF@bqd%vZImEC4Whk*J7I^kOYwN z`ASRii|E#HMgHh4b`nr@ER{=Xic6eSz)fh5XuR+e?i;h5ZJtRKR2`_K*FDA9ZNr~D zEW7oU51_kkIrHjGA`HSJiqoKJmkLX=!TowHqslFMFOgYYh-Il+5B!BJ#|kcv?<*=% zn+*oeDCCrJ?{W44 zi^XGl|8+&N`Uam=Q}c%=s#0P5-APjg?ZGM^;1E}EZr>vmdsgUv0(%Lud6`r+84}r( zOW0OPKB#?_T!{};3Q~(niliZBWM6HDelR*M=}qg6pS3IzG_M@8;`!Sw`|Ll?-lj_~ zV>v7r)KdnwayV}+LyLYC-slecnc3nYw(`OtjF9q~F>^EbxHZzr`>1#qW%z+D?$JOO z*%DEcfOy3jqzIPgY8872Dv7UpneHLN!Mf5tqY?x(B%8M{BKMT^<7*zUZ3d3d%Pfpp zb|?)%aF-{~in< z^ycSv%C^fb0_64R^sJ05FK!)8%?nfQICX5TFtZ-?ohLpiK>A9>>)ZWg9y!GWtoZM=6U7z>ZGK*G^ZUokmLhlfOO6s)fL2vWx(yi<= zFc*n((OK`{_c2ze*S+5yo|#NYBxTxlow%c^J85Ed!iXPJK@ov9wKi!^chtkQPy8Jz z2NwP7ozYSnzhsuX%#hckte8xSb-=fv`JOa6EDgZym*@f?xr5DKSH8Iz8w*HbmGf)G znZ)5JvE2?X@L=T(R-E{Kj5jXf+aX;>+-`MK-YD|lFX2i9d6`DUoiP$yX7tS2XXuO< zhf&f@%p7d(JzptA(d>m>k7S|cXAmDpTMrittHt@hc>BiiI@hh;#zteaQDfV--PpF9 zq-kucvD?_TZQHh;GrQMbYwzz|YxTRXebzod=8rd-Kju50@eJPM9(y@kG=}NjIm?Po zb|`_~DGFT}rJ_*TK5IhjH%+?*V|GdRUQstb4pG;Mxea6@g@uV7xA|pVCy{OMUiG$H zC7<3~BNxH2A!u)SN2i7?<9%~Ui&VF`=$_a9qSfa2K7cIGuBy^h!X8HM1d!%g=VB;1 zYz!kVjdW)YVw4PI`gsGA>)%ww;ZxpH6yQnomvyzj2f+BhsmSk^a66hm6oBFOAN%%m zbcP{!bR|kq@)LQr^cE?V2edCW*xSQYO7LkIZhfq<4HC%bl*4Z=cl0{ zH4!*;NGCPtib5MVuJP5!YE%Nqyk^CL1)?@0xPm~we2=;~t{cmExZ+(7!n%k5QpQiA*jpzn~SDmS9 z6#Cqie_9ll{un==I59rZvsbS3*^zfW@$E`K9=)d+W4;Y=c)uMdUkGduE8sXge~B%# z`un`yU)=crYcw}EUe*QxN$wdtF2Jz+o+&WG9fMFBlOO+$7q~!K_5w@R3acvAIIufm z=S&;O0+s?5@vNuz*?9Gd`q+|m%cMa-*DT#XsD=SH-Bx>>v=3sEQotZnBY;AlGU>|` z;_8~;ocy-NhLyFeNYjA?X(36oXPB{U)%o1iwejQ0DS6~9#~Rhq*I2Gc_qY3(LiKeR zJ3Ao8<;!?Ow-%!qdNK`vj3XxDnz$ZXf?-Iwk1PZ}c0=WHR6rKoh!Q#R>skRXC$Nfm zjK}_0IsKZ*PQ?%SIWu{h_6-gjet8I=k1xsJD!`P}`3j&6zFmx_g?16lj=5E;6LnH_Y}2qi&8B zzL#jxE7qXCPA$)MyWbT;@1f_a`Y1<#T!wAlZ^5hHI-ixD;s%&e5VsvLy?K0K*0{i)d@*lWOZxbrGSkPIg6&NPIBa zsGQ*Wsg$WubVjJ=%axql;6frCn?WMp8IhV`q-XQf4~9{xDvO(gPizKL?`9pDlCtck z$$z}(-raPFY6|wX3sFw6>y=X!>RU)4llMM)$*A{T^$Mj@;aP0#?vsbOVfoT{h|k^(sJ!w?Dm9oq(s0}sw^%rhz;MvG|8brlfI2mEehzvYA?5`XMeY`IdO@4t<|h2Ara4HUvrx9S-s96+Vj-I{o45I zVCM5DluK@1?TlW#3mlH-sjt^#!7UK8AHW~Um1sRO@V*aZlk4a`qW*4+a>3m;ya3wa z^GofJ|J$?!03UL2vUL23RjiI5u?E~S@Q z%uJ@dFGDTResEw{Oe9VA;9YYM4w0tHHSv=os=0Jl=Rz~)c?Z#cMTcU{Ll{dtpP4Te zOE>^7i9TI+<}-&mg9CTOWeiwGTEwOkvmK>!41?AYQWM0kTzSo^rm54a9p_fI=zB;n zyA~eP)e+8nV^fCD1HNU81s=IEjqjghc8(zzQr@^AQ=Qu%b!$Et4f6lN-zb)T*jEnH2{ zH_%vqpBQbdkU*P;uoMF;2wE%#>-Nd+hJ^dw`9`23n9XyXkd#iaBJ_a`cJjI<6wb^H z1+P=KN+NNIYG$qaGW=wfuwY#uN601Z7u9{|>FzmpJ)I_8`gDQovXSu8M(Ykv6m=1X z3QjF8OBY*s6N*BJbuqp{u)is%}-oeg)zIkniR#$to8%*D!_o&>G~ zb7`p8GeJF9|Ky5x5gl0cQ}(rs1}wvx#FA_oPnbdCFQ*VkQT7B<$%C_t)tg0i0YG}v zk+5INRkmoj^5aj0i_RCa(JmYo7A1AgdhT4ZYvapu-)nz+oWSdr_3)WI!~F10nhup3 zz7n59S)y%?9G~O*%=grM$5kMbPY#zZkvtTHLp`gO)LL6^2WO z+0$OiCu*u4nV^xqp}8Sj#3fqz8{SmS6gc*1oWrcqb%_a?nnrgr85H6s3b@Iauy@^o zdizR3l6PFPZ`8*tk|RYVeLCT?P>F|Tr-<)g<+o19)E}NpNN+IPM&7b#-{i!pJtg-?XzRj3ZSye46 zs}?~bKd%jWi6tPT4~07f`PZC{q_9>oT9bQZk^mP)aU*sfiI{=V2cwZEf2^mfS}Tcx z5y%!Eke=D0P;^qsULfycd^)RCO0>F&;%*&*?$Ho2>W#C6i_h1J@{6MKrM)lcn$@){ zQd7-gnoIj3hcyZS$Is7azC@!XiI~^NWX|&Gy=zASf)^Oh?_i%UnZRW{7b42v{+1|8 zcM7|}v9V?ex%%}7;wQEm=P_czsLIn1zDXS^TlPqzSY#?_iFxsP6OJ04rkalzw}@P( zm%tFpJb5ya=`9-Rdy%oYp&su$Pq*>habwB#f6P^XkVpEkCcmPh^!f484Giz3r-?Q| zhEsaSk3oju^@4E?;}fuXi6eu&*%B*U;8v&>rBG=#os!-gpm|z~R=XMMfh5d2_GoM_ z#)fUL!py41YHVP)L^z82u6%mVll@W)ND`td@xvm#^@!G%fn9oKsu@eQdQ<1E;^Wb|A`5VBeej=D9YNgtenD$9>ASI)kkAbmhLl>AruZxOmet`TslzWj1F)9}B1 zjehqL$8ms;6CFxVMxJaI6!<0NVAm?Fr!8`#Kgq#KwA1Vug6>Bj8 zz=}20s4iY?vGsI!_B7~W+ixah_TF-I(xy-YyZ&p_J3Kx5;ww3yo)7CtREM9ojK`&T z5EiN0gM+Ln)TiC1#?jmXb-WAAl+G?*OJW-)8nQufqs2~thTYIXRiYTH$RKi(MpIUA zhu&hA5nTl(a$a_;3PhaLBCX+7_f_*Kvnn)sp`Yt0)FqtbV*CZ15Es-)$Oa$s7&h-Q z^xY zwGVX}ZjmPt{W()YD+FvGrh*A@NH&H#Z2NOfK@o!@grKXOq3l-l?@SfG& znpH^Ke}s9vzj#aUC{5g0Hor!FS_3pYe~U@kkW{q_&Qnmwd~%5&!FP<{yg)3JgXXe=0*x8XS;U?99IC=HhdVG zg#YAA2MW9-mn%?B0iI%5_zXzhxfGPDJ=r%_CZ2!@yr(8fLfCn$xP&$}AWK!s}A$jY$FxXr$69~!@zIj#fOmg&)! zXONkZz!)7`G4b?9RO_k`#&i*5siTqWXGNz0n^5=0=*|*>)4VhLLL#PMI$B~ge<;e8 zzjqNYdMUqd%=9`7R_*uJj5{XGLXGTpT78(&T+Y!e(x>YsbF2EOC^9;7Y~&U zLz!aBP^ZR4GvVA|(~Bx*mr49DYW%ws1DrE}=1u<+o~!>~)x6(rUU4M7#T9@?%Vec& zoa~-2VEs4%wj_;Ea5Jjb0r7G{-azv7K}a*~%_BPS*x%a4_Ux*e70ZJaSHeTk1H!fo zBPb6%YkX;(ZOv+}U>y~52ELd8#5AYQ{>><$q7T_i`9K>Or*AGJyRVuscCb+8{J`4ImD42V7u%DN`?}cGn_$XCpDRZ8u@V))Zi}clvYKy~ zo{LTiK8Sue1HNkSgbe1n3UgVnaK zM3wwGl;#R))*_y!j%tb{_X>tK@_2rPS9a8uD)ux}mAA_RMEuV}D{qJ%2;fK>0iXXb z@!UVQr~Jzh{6WK-8uQQUYPt^nz1(q34SdTE)_&GF+TyB&UQKLpPX28DWIbXC;6Szw z(*y+7@xyeDS3kSU=H$hmU>@{40^4?x^H=y38p~F?w6d<{++$@HdKB~>{2<1Z8goZ; zB7MPgcME!oeYoj8_9s%e5&Nuk#5_9=jNzMczt=7A_BM-jW{6>BEB{Pm)zfaU-mgup z!KJ33Yat5MCAZ5te${_BlN}I@;w>n=iBS4x;!@g~`q27ep`8N^Up_L-EfVLFo!HSf ziAzuL{&Dq@MUvU7)3#L>Ll~_r-LcL9hQFghkNjD&{II(XHvc=zNflH>IhAIIQ+4fD z3*3dNck|Qp1<0;TyT9qhtQ)FS&)cpV6bm(rP8)YxJZnFpn5inxL5Y*3!_^`C;715Q zbXA+SKBk%yMZ+6nrJ2*RvrVVp;=s4l_$cPUxJhawF8k z=VZShyNot4zd7K@c>rGFuaDj2@4JbAdh9y-PG**dM)p6$`B?tQY&wj9v1`@O7v(&f$#j6af~kSj5v-6Qbuq_?P8xgn~cEPlBjrcM8n`peJSRW z3eZ>q`30jNOmMVO5dkQ7yj7jUpR_Mt@YAsYu$E6)Ct{}d$N)BS>Q<+H$g7&DdM0T~ z03A84=S!Q+47*1E^FH4)TtQd_b;O~AA*Z;lzC5ub+2KZ>ccEj)Banq}AieZtHo>(i z7J?75)u3})0x{(#s8?i=n_B3!I$WDbx9rQpma_cAo1(q1$64g|7YV;i{97cfSmG$3 zkwc>zZqDun^2Y5UCp@{K$)hy2_MNC)!KohZD-B+vCr$kB32rcICGKb|>O&6XN_XfL z(FJP!J-T6des8!=@p|tnyeh2MZm4dTEk!)6QNl2BO@(7nr9K0P2b754owCSaLFP~jhaqew0Y|i*Sx{%-Pq`F7K*0$6M>S^stUT3#U zUFbbYH_+{aE0BvcA7lQJqMJ{IkqHE;JR|XyXwN* zm~Q@_hu+U)iRahMc?!u2-1d!d@P~lW#|{AJ04@q7$&!LL=%D-;tR&c4kyr-oF`}4F z6TH4JK{4m0Mz711#I~KT?sgeoO*_bF=7X%QIlLsC%Dr5L`d$LHoWb6)r%WyNY_e6f z1smATT?gxUwH~kV3+H$E5JzO+m2A$X!M>lj5@w^0|GpfY8urw5I-54MDe$=lID`jr+?=tnPo&2>m{3;TJ{^ z-$@56Y+?^x#F))#LBfJY!$-YUo8Ju|%%~L9l!Hdk`1*FOE%43GFf-T;KFeyOb8N8Z z!C7E)RKJs|r0zs0!2~Xez_FO>uccgy$d8puF}kuqf&z3S3JRrMG6~G z2=@50ihwmmuFZXXF(l^Trp|?cNqbWFnxIcWDr-NrV$zlJp+lmnjh;BU46~-Oe&z38 z@8a8|a;g|JF4J@6MQH@exbI*_l;WTi+<-xwh;qK~B^oWXQ)V zjiotN?_@p;7#cWH@GObe8Erlkv&R?DtjW6B_S$`C%H0*nvwip(3l+qvOhgQ(64UiY ze?7eycTU&_JJw!Y6&l6d#8*^n&0M1+A7N;)xj0- z!A`W`K|scdij(*dLypL!msVnIQPI@ELSk2Z&B|&~NzX!obx5S~+*6)3ZL7{QPU+CC zTNydbr9d?IqPy3T;>GZd&8{`AlqrJpt--RlNgtW-f{{QiYY4q11yhH_E$NpIT`TgY z4`dzgpkEUWrCo;J_Vr#GM6%W#0D$*n{* zt2R@P!E;JyPQG&$-U!SV#;Ms{61eb~SE)phS&E7?(HRV~C!|;8HF{gdX8J9gF2_wq zOm?p-ti0@Z8*ya`vD)p1G8)%F3FIEgHM=b8ck#2&HN~}DBjyReH&6c%hge|im=MFA zXGkrN*8+x@9XD!|yD@R=Fo08sF~rSbrHL2U)%cLA70woxREfLZl}X@Vhd$Gz{8~Yk zP-GsxBdly(^b9wgd1{rTDdsXR%%v#jRHM~3oF&5pz zfdg6*@{n~c8-7OjSn(`JmCKTao+SN-CFBbd-4{dl^{tohm?TcBPYIy1I?5z!BRs@41FyV&WbEgtHJV&3cbg|9WD@22^w~`T{k5 z2e6gDr89)+VZn6(m-6M8xRL*miR90;=XZMDpSDv)I&$f^VT?>c0@Mh{{gE#SCqzIR z)7rW&6TlQ9aKcjzO@K{PaV^$>VT^M@q@8$uk`Y3S=&Os}k*z2QDQ#PEpJNI){-7BM zLgo+TArxf#L$1khC7_Y-LpEa$JQWRvrrpGE)J+po-c}PgTNYcl`|z(y2tstzkvrmh zKNVr*0jP|*#=Wr$nO0tVBjObEPNjIFtkr>{Q7lN=f^cRs}L%5He9izg%T61nLE?(36fb3(;|&NHU)#ESBs zAX0Nlnc}q6)MMsY@7d(8I`F%JPon5r%_LF9B(6T$l0)Nu?Y*3#2!SCJ)Ty^|9?@6! zwVhh)>G8~7o|zg9h3~%D;YP`C&6ynB1}N8dP-UgdoGCt(n8bSR*Yq}b#B=JaYS*l| z*MbaJfi*%h~xOf8fIiF4$2u^5%rx#%!I+b18}VU*H& zO9O2g>!he!1+^B;EWKuaZ#A!0cbh_tyvd2m@cYgwOi`GIwA61sZU%Hw@bu(#j9_Eu zTYb%OUN`>95Z@m*KJl4SDD5@~pLsTHxa0Z$dTNT+WzEkW&zI~Z&9 zP`RF~Oa%K#F#{QDVHVp@{P)2`wZ0p#AM2}eoScEt7x1OdeEG|>L>=bk91k8Yo7skB zs&J{9y#OO~AU20zB^<^cFv02zyMwLu$P8uE(JUl}!ypUh7W%jI`l;SolV0xosb&LYC{u1;G=dLpb>;K6~+)<92u4=Ue{ zestu2`@_=P)YEKG0rF3QUvhT-hvRF%x~sq2`G1tXxlnq`0A=sHc9>hp(Vl%_xR&`4 z2p>|4@C7w`P(6H34Xc6ph^9Eeoc4D8YNgXkk9$K=CNDx;&K~;m;IoVh`6ceIOKMR* zbAnJiX6C4LmLv1qW*GQrre*&dB8NgF@F=EIZy6CozVlUJox=f+1~8tzwlfF%fHsR& zIIHVg7BN-nG>Ez5>EzU^OCilTcov&2%ee_~3F?Kv+QuBOA<^hOgn>=CSvn04D=)<& z_0B8!z(>c9hjRAruThN3@Ih}hekwJ4m6cihiyx(p-T{d8D(=@h=)~jhoe%?3=>fUv zi24{i<%aS2^0PV4<2wZPJk&qnt2SLB!Eso?n{sRdwFLn8-1 zE?DYarp_AWJzA)}k*V*sxp4;kR3Dn} zf!Wgal@leNuaS4OXAD7)T9j%|OyH^!hUvv07*3u;c_ z7lfSDciV?_uHc>s$n*_ohTQ8Sqf%ipkp_W;XAgCMI0F()3!a~wI8A@)4F1MY;-A~X zjST;&YFS2hNObXG_>C*z#nh*b{3**h?x0zMf~UlSJ}CY4`cWc1?rFa(4zy8qS?hLY>Ty$WMB6;tT+3EYehnr5o-H3ln+OULh08g7@N zgmu#GCc8Ppj3o~9M3NUh`;yIw@1;BZxu-dzEIH3N$Isb5}pT^)++m~ zIsgs&+^CA)!W{Gw-mRz7Ayv8X!Mh~w?uB0;qyS#IJiVGjOG3x9VS#gV)gU4U_Wla&HF1ZW0O6IMgh6h3YyxDLsDg5#>3 z-h`E_U=~pCJ0b+UM#lr^w^ffeY)iKqxPwc99n^Lw!Le*O@(((<@mC{{2FKiAI~$Sx zKr`W6r<-h+;HYsA7{|$#P|^ElIx3ehLz>wdT7*Pn_c_Vc6RvFYg4Ip1TMEkBVAFjq zDD`7=9gL9VO4Dx;uurtGPn1{0-F5f!Sj2|8O&;yK*-2M=RzE zo>yv(DiVF^n@;%~YQ30-jfmbJHZtbsihIeLFDyTUr)UG5A0}GagPjKt7jEbj``YQP zuy(4gztNe+oUU^QY0~;1WeVwlZ}mU>20s_0{IzBH8*v~mKNqfBnmIV?=ovceSsVN` zvU&j15H?;R12_-x^#(6M?MAj8%|AX5j6Q$&c`J$3xxPs$1o12aIN!=NaMiW#ZEO-Q z;+Cde0cH_9XX;&3M<1m8J$k{kvwZa>L?UEs>0#=set9-m%%W_S`#0=8;z4>a4=3ld zn5h@{*BzQW`3VG$5*Ond{tN`ycO6j8HMh)rr;T<>_XSh;3L0ORX6e%(442;xE}My* zzRCkOB&4R}pDC&W!nB(v9f8Ozb^~ZlqXNh&cE@Pc@3c=M66FMK!!Cd5Io7~=LLcC% z{g*N|{`>U&Pi3lO;N;+FV+9axx9Ug)Afe$2m8Cx?%!Kaaxi)Txm}|*B$%nBC!TCy% zbx_f*L8=vVDFFF=3MT%dQ5ajJRSXYKJp$YIup5{Pvc;J|aE|g2fmp#yZsikW>K9WK z9yM@nld~NZWf}*WvU>!j`egRCR!&7~Wo${Pg*j#A`u@r>r+!09rXi-6!E7X`+i!mJ zOL?caJrf!$e)5hPX_%H}gp~a<75dP!T8)lvsl8buJQPNegY^b>aD9wsbTi-DP%JXx z6vby-O&Aas;d8p_*)k`i635RelJIeDy66RH9#2fprv6em8sRK+Nanz#AORAVK&JjQ z^~n~-{xNp}n6=q=%meBZZqgz#|uqdQq?UM$5nRRRV>1U zKKW9ouKrjw0e|-yDMt6xQVLLpQdp$8<7KxS&+68dFwgU?m4erRa4o;I!TrI<9yC3h zQik)B?e@0vuOL3sQZ(iP_TgxeXI_1sqCfB(8PJXiT93>3!MFIu_2TE$DC6P4KkE!R zI`Niw$HjO!-=na+emd15fsvF_wMd4FbM{|?k!whN$hKRWcp;>DV@!g}!MZyPY9kUg zww=0JsNf@tWeE2!dHv!;CN@1{)ZnVnuxpAHtbC>^`r46&#^6@@O){B&j)j&=|m`7E99cpDHyz9SLEDP#X&#kTlx8FDIQ;z>dKxi&Url`&AgX44hcDb zsD#k*SRZ@*kGg~^7rB<6N0gE*)`Zz4PC)0>(z@If(j$aahppw-7vqH_V?7QbEW&Ul z`;Z_Fk_~e?`9R`#D~oO6nF$qcLtFgWpSK_nHdUC3wE}6Q8SV1%7**Q{;)Tn9(4A9N z!cM=hLBj?mg<&)0f{sraE+|+#rJ>?L_ztl~nZ9It&9VoykLVL{n2zeTV&t@ zHMU4ZGzHuqK?Tdg;rpDH*C~+q3_JS%0==C_cq(S9;9buPuKUMGdW{T|I!^hNZ-O!4}TLex#dFY+;b z`TD14cLf(tgvWf@eM6Ji9c50*J{241UD%@ApQUK}S50GKEPwBd!BS*fe=b+8xk&cV zWOZV_h<+7%SrKcrw7tGE4}kS03zjj6A|fWcwE4sIh-?jT?g4VK;$L#C{Tu`Sn_TR# zOwS*#_Ge4@=K{&ef^nU52(Z-q@f1CTQr7Y%=UAhy6Eq3>LM~?^%YpdbCAf~YmVS7d z{j9*rP$S7RGvjpcG$jai5JeBUK;RDIyvCE_pH#rgp#T_?MflU}lf(i`DNrPu6As?c zB8{h8kj`39p0c($vd6i$CNd=i5+UB%jw8R>40g|~P2~xe>sNekSHDZ)>_yf^1C3UR zoXp`#z}}1194o~FR#NeY9+s5C_YJ4)hm1U?@;-b*Q zpRO-niE+RZ)3ou{`}ceyESNM3q^ZYm=+5>kZ?of9R3kUem#S0A3H<4QF^z=;o!cqa!{qcb9ugpg@#FdDei$XMAM@5aO|@6Ua`>L=xH=fP%vpSJELw2 z*+uk~Z`Mf@(m{zz)TMZ7ENW2jqw0y}Z`}B?S|wd|YE?xdfbGQ<9+Wi3f-k@a{`zOS|6A@MeSZ!AGwK`pWsCYh zWN7%a+~dzx(&}g2VP{qXLYRtVLn7&+qK|OA6P& zOcTjlL&}>lG_i;uuP5hO7BzETTC(Y~sM*m{B;?E$a1cE0aw)B|GK ztT$IyWpe+D9um+0l`P)VnpG(dYWH(Vt%P#8XnkHF?uO^p{XCvv2$FU<84_0v(?nN~ z>!n%BsOQ41+XqQYIV3kjp7=cdy0fMg1$1+Ku3Hwc&O;Ft zDXpRYBaK8QfdcIZqCzJ(535+%gL#8<_?(z7WWB&VrNDZvh-*n$X}g_2o{D7Vdl$AKyk z$p)4xO`LP>uZ~gPh2^Qoae(jV4R@j+ea=?-KJ}T?WCj9cYV%#wyHDMO;jXN;V+~rh z6xU6``aqdgg#HUmwHT62$nxkk_jtcsiJrM0XGOp{_W)W(f1QW?jhKc%w-RnPj%Gg% z17IK45*s3SXq*1JAOSVbQ8_CJk84Xowm49eupA1QfV@F)xOC%(_;yIZqMwk*gsA!u zPdZS0o>ka2>`S2qWNAdTkmF!PFD`MTcCuAiuvmI@@q0;0dgAUBlI2|WYTZi=Qq$4N z^YPae(o2;-*^R3tM7E~BESN}4}Rr6YnbM~7a+`3+_qM;7mTQ|TO*H6 zOrJ-RhW!@Qa5BROlYCS+=_zix8s?kIN!bbYAa$%XN;`5P!9W{mTa{6ZsN6H4zDs$uo$~Mv7tv}SyT>31ybdd%b&&%$QhM$C0D>@fA&Os7KiBc#VnaBBU$e zkgc&z^r$9P9ex-uc+bi`)%-5E+9`f)CmNe8eR$z(D3UAaGTdvin`Xzlk$ZZd7{ z>JzO)^ajnBd)lo)3}s0m=*UaVH_-qkgXI#cm`gscGzI^)+6=wzJt}h+t69!uih#z7 zdH7q7TNgA&^np=wb2|3AlP3>!Wv(cTqFGkXnb}+JR;h!Jiehl)nFQxx+4fGfGjWXe zN*Mv&2zs#bO7scT+ihE@N{g4?HFT(hF4g}UYRy1dkL1l?FB z@o>D`I&KFR=SHKUZ(k$nR@M~C2CwX9-~?ADfHoKA(PpW$kk!XS43Rd&cO4=o_GcOI zH|L6t%oFM#$a)-BI~)_~RrXT>Ap>}5r!pG+Pk7CTB7?5m)J@)Bk!Ri>XR zHGpBF4AuIvPwX2Y+^YjuKK70cxPP3x44N1Lj2Cx6#5@ zSJzbpl0fq=lJnd8_(!cXR|s4L%rX>n+tcq=yYd^elC+)Xc@{jdisO0sq6hB&Dg@I@ zcfkcOM@P4w6Qf0gO=QP{*JfudGPuL!@-y`_T#C{C-10unGUm{O|e@Rf;Dx49Nw%gdM07d!vE1A$1uEq3>kSm*9lnBd1nTc)O|`}R}A_Zs;?i-KLd`H zBw${TLZHe=m?T&Zu@|-Y9p>QWi?bk(A(&B~^f|qf&E$G*yDFn6a|i=WTc$gR5{Q2c zjJ661eITy0uW8Ol0=*rLEeVBew`lz%=7CbYtwVf*`8HqL#iPuL}a_|9e zMziqxoAq1j+DPVYk$4k!Yb}>uch4 z_!yc_W79`^2Vp%c&5yw6pbE76hJC;>aue~1U$R5O;B@jev*DYW_U&#)YDEypXRX|^ zgK{ZNsJ$06}^61s7h*0^RO#)B`h2Iz3WfO7dJXnPY>5;O9wY2_Z~eDLDO>_ zhRNxnxV6&QL}mx{1)bE|o;1Cmxl*XCPmQn6P&aW?bczLn&PZ%qUmYvmoP9hA>0R{d z)C(j%CYaCYbBbgnZr)obP97ZNbu7q5?TbBT5V4?KH5F@r`M?3se@@LF^Y+ITgVF}a znFl;I{c-|H|G(h@{G-r=rS^VzciZ&v9`gZ%{6B9)pneaEs_g?n#IUTyaRV2>k>8e?_0 zrIO__u94J!>`Nyisq;9zs=Bj~!~2FocsFgd9qi>yOf3UVDQi^QBP_jEiu{PR2G)5k56Uq;qZGv8^ z*BT=1+TuOr=!q%5(^_8BM^xFiLG_p|)QBYy3i_5K{6gF5sko;d?xqYPIPv+4k?F(4 zX<~+zN)buRYLh3CS!l1;3ia<2f-c@(NC8L(51<2oEg>VoH~yascs~K7270!RPWDE$ zzahMTd-y47s~TNOuKEf(+d154(pR*5hxRy%5~3OpSu*+ElhN=3R9$ zsb#|z+G%cs%(b#wvFMC%$=vxXmXyBzpCdeOV}Wid_A~pBC%mS3dVuT*4zEYUbY9iU ztc`RkU;17q-`F0Z=M+8mywl{lw)7=DV$7FEfODp?e}H3d6aOyCiFh5odVrw(ekscT zkbCHV7M}iBQT}cr!$vzKK1O?RQpt#odD49gvZ-%kKM4L@R zr-jAofW5uXpWh7a$EF$6`zvy&O6b1u?kRzC%4yVxM7Ds-HOg)oljO;<(yC$?#)q2H zy(>V&#j77AZaamJJ?Ca$9a&GeFz>lQZt)lDlR3W(t%2}8%Y;T4LFaLZ3)F00-BfDQ zPMv%G&cB0+f&6yx(1Z1knb*=Le_{Z-ts|6L1b0=|HG=%A-nF}Z|7^Fw3da#ml}6-f z+nS^F{GLjmSZL}98qd}(Mo9&?0>esjH}IG}H3O{{Ef}!5A)qQ2VUCDBhR2^9Jm-z; zT(PtxVN~oxN?Q5wD?$+to&iD?&N%y4QoeaRn&=Fzq?n?!jS&+rDl5*E;ladF=^f}I z;xJ-)RkefzY41Gs;W6UcUfAoEYWs^xKT(CSsW+6#(}|AFJYyK&15ZK^2+bCTAw3;B zk$PtFL`SvXiVE7D%N=VQD5qc#dJnQf0(@NW+@R(bDn-uJ`{v8$NH;qgz9IM_rqfB+ zeK~iaaC7=bSLwd4ZxLM#`DG7%>h;A3`@W-eb}CGp@ClK>U;5!2%X>D|LcYN-EIvlNy{z+s8|6R)S;q+|A+Px)OZaZp**xK z0S|six7dXqk_s&kg&@SY7W)`DAcX{F_UlUQrK{bokraBOHY6CohJJeV0Q1nWfk9JA z#L6$6l?v22CO~A4A7;KXqMiHS7Wh@uXgzvxqK;;fj1u+k)(mc-;NouFxG0*-;{@Tj zaIR%gR{e#y0>f2|U(LcnQunqdrj$_!e!P zacG81Z;Qv9-hghFh1w}6Ko*2?zvc6_&>2($c^GlrWK>mk=d~jDX@+6?)?BzD9u+s6 ze7rH#Myy8p^oE03epXWBcJ9Ea!zl3(W6tEvj+n3s<2s)8=)Fl-=Dn&H521r6Gs!E` zjO$xHBoE*1avTn{i=`cn4PN_bdyj5tnuNI7ol|<@(2kxz) zZ}`&uxk~!hNh6B4DV`2^ejWLOQg?L8DMP+dh)dqlYlF4foeOG8oIVK}qxFr<+Gu6j z8%Eb!L^gtFQTLAZy1Q-FN0_s=uz}0b^Of#osaD@vkt;?JA@NGx8FTLO2c(a`7_AbBAoJ2}&qoOCZ=n9-jXfNap0&4k#N&fL5jJN8mvs?{3$rIZmt@;C9)6 z>2@^$-}rwI4*zO_mg5K@Qvk3hZq%^%Xnurj@pFB50WA2a0KW7gO)8#fRqNwQqZjX5 z5fVNFqPS>*|F~1Vo%Vd{OJ28;{_T;?Qfz<{EbvzI_X^QARg-?Ugv4E-5(e{0@=}tV z4;U#aB6DQ&J%*d4U^>v9Z0P#jHpoa*^Cj!Ps6xDF@oeO($gNN3eLB3UYG4ZD~jsuA& z)(fB>Hpw0=!Xtq4bMl)%)$#7hCih!aiu8x#X1&+v3IOv`HifEcNSMJxr5=x=_<*Y6 z(=nMTER2TK-{@D0WpBl2EoV*r;UkE|@ho(qWOJ$6fKtpl`B$DtjYQLp7XW!ab1_ee zO04{v^}z?L;ybX7)?D{$qz)<75Gn2{Obxc8INpgoM?IDKWg)N95Ul`kg}bBgWh86) zCp%tQRiWcVQRvZ)!8+&bQ^HtMkSo*B2s{qK%?gu>++phz#3S@rxr5}c=VH7eU&;&_ z%JEawKrLyJ*E{kx$29q|jiEvfeOMs1n5$LZrPbkc-qjuJ9SWnS-9Rq*-xxfck$dU` zE3$FJQ%cD~J~&_^2AV9i#(FbW(rg&jtqKmH=C_ZUq*@)|^0XqCzaWbsYD-4~g-?)P z)>K}dr+VBY)G=O$VJEM(F*?8&&IgNphWPz9tUFjoIRkD&-Y;Rne@n~g7b@_(hdhpz zx45K32`F>$5^imWpb%^MKfJwjaOG>eCfps{b~<*_v2EM7jgD>Gw(WFm+jhrJI-0fT zJ$s)s^PP8pRWrM0)ml|+Rr1g8d2U>{uA9z{A?w^ivAh-a$>#4>@FTOaw<2fu7e zdT0ov&!*GrwfM*Mo!u`fZWGIcAX-RV-)f7qP)R+YsQNaRvkyVTs4t}9;UGd^f>^_2 zbnDA$83xH>VD-JNlWJz&X`I<|IHRb`5(Ptn%L5wgUzt`7PI63)#!oosclys4yltI)MzX31t@f!O^NY?zF(^#&XSgrjy z+r!hT#v%TVkSdU55(R3zo1Y6|g=s zEI)J95m~oaOwHjd2nY{~k{*;fD&p7C?;k>|P)5;^+H-*?b~};?%xgpfG9c;&BDaf@ z(E$pQB;i866<6}fMnMa;nlaIORNED!BNvBf8T(%5p3`zUyvObNX@hLh?9=6opr$E8 z@+K+ci&2Qz9kf9ba!(M&)IXU%Ed^A7rhdWKQ>y{_VdV3fA%W;Cm#PTlQi50_HI7qI zco7P0C7j>2)IgK7rrb|NtO3J@bpbI`Yozi+^gc+x<M91EX?3mOz_G$j_Of+MI(1Tw7k{YDbyiyW5HIGART>;Q;|S{|}z zMkt0G;Z7@sezrhX5Pk2t!-q~UA}3htMjDAt?TxAe^Z9(Y_LC+0B@TiVq(KubOB6`n zF&9KT1B1VvoF6lpdMGPrASb9y^$ zb(x7>Z?SZf8s3i!wf_))As)<_i&h@@y?Jmd2ON#_^PxN3THejF?)jYdkxco?%h9i} zSo$8$dr-Pd^UlY}FS?O`EP*AWD;2!d0DBrUmP<4ltSSZ3ss!F+@z*`RAp($z9d~>s zAB~((#ab6LZtfbb$xM!T13O=?cP%5^FKxG&-}mryJq$Qo-xSfSmdYCD5XW+(@aaQF z4L(jHE@MlxEw5uS3qmA2z0|{7=!D@Mo08q>*b(W=ho80q-ts>pc+i)z82}Ii|J(f! z|A%{!{(WPce^B}v;gdK}*MoW2I8(KLh&q6a?0#Tjyfk%2scxFoin z$(6JV7=$s`oJ=Q*+{@XU6%tl~kC-=|&&VO&)A+nZ=y9e%&nyaSM87YLm=%HJ#>Wxz zNP@oeO@lK5^pnPs{xsUA{?e;0nV948Kf~{s6I{k37rC0X#P8(bx~AL2Qls$_@kZ1R zAh8_`!$KYrh$(r@%m|B_Z?9%Tt-V>Fe|)=HL-1v9smNj8r8NxZqR}eSy1W7O|49$c zBcL0buZnU(xM|q9yM_>>Uvl!bH(!Ju%@u<%RwX^odIXHaTYz7tx#5BEF=kjo@_!|K zmSL7q(()6+|L_k(vVCaq|1DZLjH2Qz`-6*QH#ZCC&I1$)$-1x`k)WuwxwK(cWj{^$jDk&kSp#?=%-3Zm0m$M*p&vQA_-M z|7DmT0|vYSp~dc2DyR2j{-<~N2Gi4WL<-tqzV9GA14Oel3@u^+5ncwyneF z$G^5r9b7#w6e*>MKGgS8P_iq$m!J%Tt^UY-{t4~=6wN>Ezbe1% zKF9woA1(+c<(F@P0+b8J0dI!%9Kj(dNsQ zZ9BB>$>_4o^udn~;h1cZ7+AZgAqLBRP$!0=o9V6U{{+F#9fa*UA!jaCU8EBpw8PZJ zUqlkYjI8x;iU>sj>PYD4KQJ6AE=7Q7-z-o~lxt3zjj8LaVK{L_T|9(4fMXQ)7+6~z zUp+&B*rjebz9TVCy>u<%uV$n*E^Q2-dUdd&PUZ(Gw{7;gby<_pQ_SP7o`I~50 z;naJ0!i5(+n9%~`!6Tj+LaXpJ^tn2n7W9kM;S@?3>U^ulS=3;sHCeg_2l$syb^_Y# z>}e>Q@V4>BbNa)6>)1m=E!t-$W*+I0byd11B50Bj8*mX?>w@o%I7{ud8(ct73H@42 z^2pXi4a4hI@lr|u0DjxT0Gdx!OtQHY%!;x=MJ%Z z(%T#2rNEVFp{JuUNRU^0_k8oz)59;qsE@=n)Ox=f?bYyPAntUt>J_W({p zO?a}$O$&iqjsK%D?_cgjU`N{-AAsPZ5dJP4vjJ%NKNXq&z1IHaP?Gp#&r?CVAwKaIPA4z-u*8u^(C#WH} zm$(aSQwZv_cRbnY_Rz_t6Q0o7FW-1Q`LF@yPHS+rLYAn{4*y6wFVYH~(p@2hVhe12 z!dAQ8Po%UvSm1YSPPkfXKV>S6T9z+r%6Z|NpiHg1%K{@m_%FJ!vny7s1HXHC@M=CS zpNSBqHkO+wK`)C{VMS^s7OrYLWDMvemh>`73i3iHxP^n2GUckgY+=TAhzL0*9szT2 z1*szh&0E?27^R|)du-E!g(pObq1F4G@E z|8ufn2Ht@8WOKEx03C-)=0SpES$HTj`sVANV<}4K8+Ovx)qEL>2UZID;jY?xp>5Wy z4#y=o$6CSJ$lcZF$t-q{hvziKEJ;pfKVBBsPt)wm3_AUqanbBbFGIL&YT!kBZsoY5 zuo}XA?9XwGTTN&Zjg7PGazTCy-6OYg^3MmK*pbCVnQ7O^)nAO?tDz}hV?VL#d!5|J z7)<5!AUcc&Lb!jk^Mt0_Co4*yeouH~XMbw&G@9u!|M)B0vW~mv$_9W)S$`|jTK}L- z|1o!J?f{sG`jadd;C!qm7yy-7K)!{;FXHl4VW( zP_|C@Bn zUuR+dzZ^%Tl5_=tcmH4Qw}hzs8^B$Oa&AFC1vF3cP_hkckeiwh#w`5-9N5D3iLDQ! z36dzLE-p-*XQbmk#$a1%Bc4&j84%%YQYwuP~;c0qSEdkTd9SL9&$Nl`r z+EL-Td}Bt70$@X*koA1R@$TeYq4L{13igu4!TYPO zKUFTYflbz z7VM(I6=>o9GUC)o*(QIzzxZkU`<69M#iVv+{I^PlEN5 zb0{R^w#qja8^d=gub1`fWC*knPBy#i2?-tTdr_y3fX$d720H5TzRa40T(Ae0Qb=<3 zI=^=S6Y0PFLGKSc7JODDk|6X4H$=u9;Cog|`fkwsN=nQ%ku65mAp0fC|{DbJm z5SRXPr4B-%a_-7}h+WYonw13>YgNQg9ZaNl3Iz{yn;lle>BwPgd2brH5lVGVro^%0 z#UTb6$Xv>%*k8534Vr)?axHLXeCNs)Ng}+_q%{r0w>iw^2F{vjJHV3Gc~ORV&>u>u zSFe3>DG=MGW;uu?$(l6inSz5@tpa$tu&V^^NxS>JPK;<~uJyzred(~ zgW{j2eV;mXh(+SKflr|YXWGJL(pv&kqbG_|ovLv%-o>SyJU#eX!$>Ak^{l3KY3#OP zF&ID9lm)$txFH#VAkev0ldwJwKT8?{8X1G!XYQGOF;6}||I8ATAPoi``}4bs1t=kx zcjVm(&(s^(-ocQ-vs32Bqc>~!{VU|fZ|&{*><>WO(!WHe6h~Gx4lsf7wFh=j&piFnp!wOHCaQ{1gRYk zV5A#9o@8AjbAWvzWFWs(lqde1_R&4FE+O@*NC9!6wIm_)0C74&Nxq(EQWtqIS)fn? z>in4nLy!(`!ovWAjov-fR-UC8l4!kM?a)E#OU|u zs834Ly4IR8hXBn3j+6&ieyTet&|jI>Ctc%?tTp~xhhv=1=qNsNbEJQK{qk0Wjv z%H++dxlNj62zkGk(8~Ar7tF=qpoZB94K`KU3i&~~-*U=M^))M%RAru1n+AX*Ig_*j zb&1qYMHG4Q_Svf2L)8V+k+GAaamwV{kJ?$E2iEFrzT8ozSY+9HZ*6aa*{J);ewe|# zo7n^1^zY{{r2{pv*5&46AB8e8bLuw3mX*Hg)u7VaUtiN-)!sc`qv-QLHn@ zYALFw>9f;_St*LTMP^>};OlpG1Q{`X9;IIu<J;#hZBTM%CZvstoc z_IIND#@MikUid0yN>JiQaKV_$ey)2#;&-z6;mBvGj!r}jx)_LR!jdGyf1!%h&v7J;%do$6dzm4HuroIH$*m-R95hr za@y1p3gtX+lqu=IzSzdXTMfy+CqAurevY&2DZW<%XK`s-R0FzNfXS4QTnSCEQhgJa z`MR`EV98^SjtGZvG@}a_509-kzQtI7cgc7P{vg>l>SZZ$7*)UY#`+&C>CNFX^yi`fSpE(Iyog%>Gv%CER=tV8bRGOOxJb?6o^OHGy*EnD*kMu z_j<>a@L+u)R6h>?mvldUfu6ac&y<)_y)-!V!Zxf)5Go}LJpg|L%Wvg9lZ?26;b3!t zFCfZEs4A05 z23g>2Od7%*FQob%KUv!Ehgg4OF4ubVTQ8f5;08As-^4KRBu0qds;4_&p~-)6!zX;m$xoWL^Ptx2XgNRn zwhbw;jT=(U3VV%dlJ2B&Ru{$lc^N!40DBKK%E1tJ(V@&6esA%6u@>?8q4VSFmf=V_ zZ@^&!nR5F&?uL3HiV}3I2|21O!~A3$PdLdNNhKcbXQffHxf{5Rwp2aVh zQ5)>@SMjU=TN_htZ0PFv79nF86zx^+{F@R@`jOhi?H2}(<9stVpkSw}1furw;&WNTn zUk;hHHdIaBy(vr5e5n7Si>|R8GA*o^645%f%vR`#O6`Wv9u>OHfm|kv7I54!3-ttF z=97hGk`jVi3!U*3Bu!H#k*zxZVQSP6xl;+lUfQ*H%ldYxkAG<0& z<)v+J>0x-5yZe6aDu=uA8{&f#({ftRHIGwXNbtfK1r|zV=0VTFK5370_@1#d3pZ6GHY`3aK7+8X#pjxBZa$=i=>Sg#a{XKJq}e89b(Y|RmDTTd zB`~@notO@7#~!#1v@aqdnf)<10KvD!E^i7tG;_zpEQ92&Ts-e76jfpPlf*y)^CmLc$ z%1;aQ+DP-K@fauJF~w|Pa$ESu&kERfuB^RG#@6c!INSe#9n#_Kkbt|W|667HV=nyv z8g|$K#?4I34fUM>dy;ek4L&C3e@^*OvLf*O!FijfK?xMHMR~{vsa()Z2vx|fq`db4 z*WpdwY#hVSHQ?h@@4EU0I*{z}Fur!{hIRXCGO{cEhJ75Nm0tuy*dQ;7(9G1|A?3bR zD%m+kIi>y!}%XRFOs~<8xEweTH=yY@z$d9-SF_?15$P(0;75HjB$Sa)-(CU z(1}FXwZEhwzFH@W2m;YeBz|pc+3b^$qcvJ_E@6{mOF-D(exLu^(Ht1DWiJBFL;OS1 z{}e8enP5g^+83oZUye$R%9KFgGy3~O1*H3PJYsRKNzW_-GLqftq7!hV0|>h&RN4zS zCu=yfnKBB0LWyk{qB}(M_EIf7Op$Kz0~A-)@-Q3Mhq*7c+!NBpmbVEBgCWA4x$8tOoi_axzbY4Xe8&+ zz(yxPle)k;4mFCtbKfOwWvr~nt7yj=$;xj=H|4G&jbY+ObxbBvDbP*jpz-~(K<(hN zhxMt7cJH^Az9@{RC-0eCcycMtuMxz!5V)paQ??GE|Nd?K`kL$YV=u7KKJ7w-A2mW# zPoqF;e^a96VA;I%RXdHWaSSthn&V-3p{K%%H;Q!sxMcU_@a4va9=FM-b_*=ZRn}Kh z<4m*66~bNNBXRXbj>gH)&xOK=BlYd&JI?3(^Rk&culF;^O9&H>*azW>_)EgAfCU>p zrZnl(e#kU)9{*8*r`b++qd_W>ZFAps%d z8~R*4>ZL^3uIPr}vydG642CgBitUT^!diE5`)=LG2&8P%SHO5BC`Gs#wif{3|3~Qj zSupVkK>Sw#&-U-l;6IY7{BKn6FKZar8xBa#1y9c@mI8xfZXDRG^e-u!n<0m+05*9= z)(+QUiGH6;MN97Vh;uz7rS5t-xS4oMPy57nc3dJui4v6_X={s|N{G$;x;E4_l)c33 z-_V2UgbtK#w6ubsgEc_a8acdCx@YUAC_)k_uyDmahx2Qrn2gwcLHw; z4@b_^N~R+^U1-B$&fC|349|e!y2U`r_5p+=R9#3)T%E2v2lnF$r@J~^yg+6_Cx2&j zjf<|j1#!Nstm%REFAZc=sGr91c0*iq2Q;RtQ|cAsbj8AG6;+RU%K@Et%rCP0Xrp?@ z1h(hmL$C(h?lj?HLt)=jF^V%BiD6v=)coLxv^zp^7NSrRzFuEAYlScb}-bZ?^&V_FrN^+8BrK1^6`!5QTp) z2LFf!wl%OY26Vu=7}Ndr4S;HxhU=jG2@$}rS3Sb9QQhNnaV#Q&6+hBYDUT7w?k~6R zqq+%SXh1jSp;v#Q3pInWT^_B${EmhBi0i2Sitm=bq%x8vbgalTEXnoS^Wh>pZl_o3 z0()Qb*K|x%z-z)E)1rSc75@7t{6}~U|9UPuI|o}^6J5Yt-^d66g8xMVA^peBqd(fA z6Z1P;yVl+PFB;49-&_v{DDwI)bk@ea@b8YK$rdxL5KG;K z4<8UQ=R=|^L;Q`|v4opgs@$B79ER#U7rmENi}C}^W^+a@5qa=5-JKpX*t}7gDM|_v z!z1tJ+)@~)QW35vk(k$y6ty*-Y&tOf2e-L7uK3gwbrQZ>@Cl&r%ulD)V$J4oN{zAG zdK~KO?H~Lm(rW_l)(i^F?MdGvCx_r6E_@cEb&Fjn4)QZb^Rk82s!#eW`OElRuU<;<*G19xL#1OsueuIRuN*WlHpl{HKs- zD1d);1qk`?zlD5;{{+nV_ag9@$%~P;0Fc%NEvGA%`uoESZeiDpg*8|j#{+{b;6&&tbl9$m1V zDHav?{A8r~99=6Z9r>{)d-i(@TPUBAr4q_Hk?cAXH}$+YGK(*jCaJruf1bSzeCGZc z;AFG@7UIeL6KC&eWci=SZa{tAW}OZBXLPoIw@+>?^fG|<2-InJZOXHMBidy~1@up(_wo`-OqgNT> zROyp2hgsF|)W{dO@;qifJYPiVf4cxRMaMM8=a^MtN`?-IQ^q9FO%39bK^_dFMyL-- z272!(ojsGQTI_lCgwEDRt;CQ);Zp6P7Ss#dA}|;zY4mO}f*5Wr{Q{40%zCWX_d9Gt zz|4C^9WOl?Cd_17z6n#gja#yy(G5mG+w&WC_VqiQ=kOldCtV449MY5%)Kfdn6xg)C z1)05x9t6Ra93k{^#O0k#l3_^s{ztjh=PO+FV_koSKMmMF(j!&IG-d- z^4+6~1B62ZNGV?P^&H5gpsur~?yYJ(8x-)+nLbO#zPcti`lP_25gz+pucqE zbYU;3dbD7yqbyc)AG~XAG92SH9KAzoYf10nc1O}HI0FqWX)h6Esvuhg)zliyLg<@K zM&WiB(!n_c#i!M2_i~+!CuOpYYL-Mv7>~a-niMY$x1T;{sz8D-In9w+O(MGJd)Xi}o;F#%!qXXVOW<0nH1#_@6f_7`x1H#ReSnQA(~C-2<&a zRfyccBJ+c;hqgS{sojOm#lvDF+ab?z>?Pykb^0p{a_js&;T+aFvlwt-RH7-2=BUPN zsRn(yx*i9q9!K%VB$h+3$t+DQc>h%25|!I(e+{K@`N+KC&hCfT3qg;)7@G*v2yh~S z!4HXZwrKV2iG}dM;-3hhCW(VL_GXZjGB(9-upkX=iV+t7^^H-F zZIW-+8Cbv=ToB1mkEeX+fs0SP(usD+!WV?cphcZRHY)<%l(?^?uVA`|D(i*9(ltgN zCWrtLkN}*;tuu;}Fu6avP9C&yE#}=BzDhNanW-u1UJjlz)b^RR$A*o!2>ea;Brpg3 zkYj4?JxkN3e>Yk@Z%1+Lw|C}>xiMEw*{W|?-<9Qw?~M}0Was?gg_fLVv@b;spmV#k zQq!FmK~sFRSezu+coC{OhZx+~Pi zGRc+{x~k%!lo{yt5^#;gSFaWgd|HzirTyo#JQdy|@QQ4~PnJ(q1A3VVePCsk$ltE= zI!VpgNs(AZ15!(ljd&CefkDsh48{@2j0LV%#gxI0lu4h;N#W{8ZSOB@z%`Y~5)Dtn zuNPvJV=*b6I}9OH*J%{~QxSLnNn)72sD^t6X#QTn`8$;Nk8D@|PdD?YOFvJQxBbO} zGMtkj8}}m}r4RzjE5ZqX4&l-Pw9r3ylUu!d-&b`w+RBus{P|K52|}*QT4GkGo-EL7 zs&4A@^Nx^M^ck@#EQo|ZJ(YCQfw0Lq1B?JCBJlQMhmM4e9yZnlE0ud*3Oc%K2h*6H zX#8LnovY}q!@oXIOwDVJ`)rVDL4Ud~G%@*gGCiSW8PcW37HbG!$(CzqAmLVCgqWim zOaZDbegX`;%JC2mtdOTIMwo@*kk-tf%->R%US16QExni(@EV>h>j_1~E<}<+8$R?9 zN14r206nzSMbR3sf>n{cVhS`7v&k5PfDP&2wf7OP-5E6>=Vw!*V5O0&QmOH)F=%$eSv0kH9@4-D!PdpoRP+Z3- zO~jAIe3XLmU@BG)Yv9r^awQ?kOcA**Avw0}^1~gI6+=xv?@pj&37U_@nAi{Fuss&U zM95Ys-xu1`7LA%~$G5W?dv8p7hb5Sk4>yx%b;IMrSIN$+{^#-Z*GeGxR*%~;V2J~Q zsRa2aC51?i4l;VCrKo>Y6!3%MGA1nE^FqOrW;z2gh z5u$h5ZdDuf@cP-FG5hP#J?Nh?>9}~|3VvWZPNjHstY1uMXRLlD868(od=9OXW$+rE zG1q>Jg1&3QDxg}xa5>!5K_1rkz-jO|PiL%m1+Z#SbXP+jy7q+?wwAqb6SNYf5*C?B z*pvKFJoC9-i+$ow1&^2kzpMgA^W%L;9=AjKt<%Npq*K6d5pZ;NOcFy@kqLSKKvt6= zfouO101k9AIc(?58`a5RW-8@NyuKwLhe$*52&x)*28k?-o^~hGWP@>Uchc4AV|+(% zya(9|Sh+gStLc7QAjfgQFgdaq`BG{$y!le6F+`u)&z1>FBVB$pa}`P^pRQvxs{WM* zdJYk}921N+l_h|Zfeos4C&`y7JHX0a<5FrDN8kmbKZ-IH(EK>2_O;5+)? znTUTRbN?rUgrl9YAt1)>RG)O%Z$;!ukexjYh;SI4BWzJRjjeOEV_{sy;vChtkbw51 zfuw@c`!YkBGJcia2T$P-Vm`*%z;&(>_3@5n=42=#0B;n+M}ib(J{DaRi&f9mCLkj` z^=$Lff%HZxolt>0|7y{YyVtsr=Z%<7GwEaFx~Fm&a(Jhk+?E@T^i!!61=;g-f%X*d zboW<`*-E`Y!J8l@lYB^pQ*!Y*30Hg3FcQW3cL^qBoK&DL(GmM`wuZ#2*kmUB?j<8a+k9l#<^?1FZ%{#=^{<#iI_8uAqX|+}jCk1U~M+OG>ufQ*4{^0!&0Z z7x$X=rp+Sb>Zx@m@Q283o10Tf?FS<*pC^s3Yg8A$S9V1;HPtP(r$!LXyHP)kFWQzp z+)z=4=EpN;titeh}EYya28_Es=Cw8)Re*G&{D}y))tlKLF%Ut2O%yOI=tp{12 z`*Si)3k1jhD(f6{iqp?_n+VK38#_C6Kjx*^R3bkaCTDgrWcXD#{rvSVs=Z;EzX_Tz zJ=1xUYwHuq=1UU(l8i167b!yx(fM8qt5X*lR;W;{4oI^CPL*sDH#HE-tdzBGWapj2 z;)KdzUoU$4>*J>KmauCXY(ZWPw(4(rPf8GOPlG)(yFyogssg?|6}z>9tpX52KWJ-4 zt5h#k3aCOf{kkA&8rvRKmUa6sR&2uXHSP-Q&5`OZtL@SxQg}fBzOe8fW-c5p{dl}|UbfhNpSuyU zP&q_WmbjU4Q0i$dOWLB!0iG5N7s#wUd#Ey39i3H$5h#*_idN23S7$1kBpy^-izpwt z$Sd-<>|;atFbHoz%0%%dv%*|OS$Q;wBW+3$G{1urnJTrGv9<*M=~;ny9T;F~pdDZ+ zqbPY^{9eGrPV<;O+nr)UCZW({I)Pc|jKZuZ=#oSwjU_USu9O$-;l&i;7uV7oe6Khd zjzeNK@%g^VxS<30l+%>$h%Hf2W$?_PsBhe1i2Y1$i2A5HJI$9LB69x(H6CbkH?DCvUtY2ba?AKnlqtca<$nA+hw>!+kO<+T_%A5rJ9lyg-4o z$)j?vS8G2ZK$ejP=xeEHAS!>610C1>Ts=R|)^kscl5$aTv!N{(a<)-bL2x3OEd(X% zn$}FncQ34QMEBW9#nW2WTf)FN2`(O3w{77FgF!o%mb+p4C@cv-#Sq3F*^|yQw<+dB zNzv(bb|MTPKN-Oa2)QT=>cPCK z?)NsF%ZR*=hNDLMcb4m9CX&+Tb0{^BNnY;l1m`n;m5esB zG$puos9GV3I_E0xzcQ8^&Cc;xVd}}(apWWjO|M`NYs|kh^-WzUcnmov+Q~UIUT?p4 z>a9402rUO_Xw0u?srOwD#TT@+YhqE%MC%I-+D95b<+?8M?sN1XZ(c&|(x#S-uH&## z&9&VBF{kq{e>`L4UWf!}PDmj5JAeF-6k`9YKmH4}Yg_<8JNjql8){k+wk5T zyAf_{PW{T3aGuBxb4Z^&6aDZLn$1hRO`S8wv~U_Q7)1Ir5=|`|pskYk@wg@f$9%et zpu<*S3&_e@gMJn2u5PF=^fJanh^OX@e3l?fT!c3E>8eV+v~zH8nb zk&t}NIW>2HxzG$Q+$tvdc}7N7o-en<0INhIuWTHw-P*IE?IctQjck070o;n)%gaCy zxAjA~*5m>d&ft8!?Q7*-+Q!QM%C8`sPJiDF23@|MFv;g5iiVEYB_%Gl>MS00!7q{A z#+u#Ks&&U)g)fC&VSJ+RTUSdxSTPr8lF&*1-mE|}ko52OsBb%V!&RIzbyRv5hRx0* zD8hs29i*=qLQ{2DvUMGvQ74SHX(~!>TSG5;la%vM{hCX?RJm$CQBVcfvQp%3T{h8! zeYJ^Mw`?jwI*2(|;6If8n{qaq?a!V;fU?8Zc9DSAFV9T7y{U;sdiqk^djZ?KrJky;zq7#-obNx8z>gY6GKl~^(^>nv`Vhr_b)b_wR_7g!IwK2XO zXwX<=+>svzyToWg(vdL^?gHt-9rJvj@_t|8V;WZBc)UBMgGFJ?m4PD)aT_Tms_>&E z5E$Z)?@A)vFW#^q3!8}}S&mzv5xPX&7V?EzIOEvVKmOH+wKCPVT9*kufB6=SHImeZ z`dUrAS!5aK2ex#z1VJKhc#Lyef1rM*=|o%7@iu5pQz{`kcxlSM9C@woTf zDqFmff{SGAvrdiGne6&$dh@B8&l*vK;ZtzH@Ea%V>wEL+OD3iM*4%2iL2uo+Xtq5D zP*YG(M0Xg>QbQdzB4p!Pv|QKQxeZfH3DU51t~<7)4WyrDuX!C-33eoVhc11-{5{rK zg6?XZP&Ahdd5&29uNAvxp+St30fb)&^Zj5DqYaO)abC|K64;P6ewx&cK#?%OFz=fUmO2B8I)?e`-EEa4?Jjw)-GXnFF} zNVM2aQ69^SmB8IDT&|E#IOQ`Je3doc90eA&wI{fT>US%Z=7%;-@5y@HY&pBaTH`th z_7sAp=_s;Z%YX=G>}t2R#v(`E{Vl-3+{Fy1b)8+P+^Ja`JX(o@`MnXFKQUe?$7Ivr zH6rLmL71N>+n+A^ejVTEnIU0Yfe~xfC)bJE{9M=F#YII|1HnCNe*LI*d9)=7AGv9! zcDv#%Z`1K(9lv?@Smw+ucx4I|zqI z-y>8RciRs6LwmdIbG7x`Huh4lB7e;2+utMD>7@~c^4ZDEwcXoAsj{TE3I)ZTKo{J< zNg`WPfY?2$G`_DS)5(*L9+2QZexc^sGKV*A@oCz}+gapCeFRjo4&k_>jR7T7qSF+t z%X<4C=H+>WB#uaWy5oZ$dz1U2BjeU%k3LA!x_nu-8MTbVphPKx-Hf+%gw@nDF$wcL za-HlLo+L>v0+hHf^P%9ZI>zN%vp*Y$(N~ zrevgKmC41ar;hd{rlzTA1q?p@@e>eaTg6OD4DcZYPN$WW8Upq41OnFWZDzQgWGTfHb}$;sfw)G)Lo@VA{4Quoc`Z>F`)%$^?_)zCa$K3<9JEIvM0m~& zS+^F(U1sP!NohH4)1u@nyU5j+I@5nVs*?Bv8c$;w7TAt=D zI)RlFTrPj-bfNjl=^^j7w3;==W={KY^NW)WMCMFm(fGyT(F>nkWS zq&9h~ntw;Uho7Yqs-W99BGN%hNw4^0XRy^}&UWS`M%dOPxrW#Z_suSeEn@;O2`d>2~o9^p9 zkY|!Vgp+jU-})y@Kv7((ava?|SaLyt>0{ku1TgrYAJ!d0JYlpb%JT6X=b@1SL~YI{ zSc@*cM+4dK8ySV>dQwOm3A1J&eQw-gTBXXS)DbX0!7ZWHv+pd_d@H##!bAu=t*M0})Rd`xYbrSPomk8miG?YXl{#nZLl@juTx=Fm7=b za5ws>S~pWa!j#qPIO@~3`{q@%K8bp1^#c&3-@T+aNE=F<*5!ZPW~IqaN3smClqe^Y zX?5Af@b(}ew=K*}Bg5|NFWIfi0(Z`$=%jaxP{e52@-hu_2%>aj_F3=J_B&70GO9Wi-Kb8}4FqPf(@h&Dw?yp3e|UP3Cb+ z^`}j`uRxam321Cdhxt33WcmL_qyCCJ5~u%Yc^dko<;mY)%irN-vKHKSkI(!gj%{-@ zK^}Kio@iXhyfXvg_EOjdBF~9pJ=qDKo5Fx8Ep7WL$}-Mcm=F@8Wl$~2+1QUhvXn`^ zNu~J2$1y1nW405-$JZ%Wil0HGvGgrUP}zLh=l2TwlsGgBIL2|*s8}bman+#!56hgN z7F|aBQhg#=;3j+pbsT4?tDZ1e`S&op^>vZcrbcQ4@JLTt?r&8jyRd0v$J;OX(TMGe zwP_IZ5Zo&xR@kSeb*|c_Uc*3QLNE}tEBnA^nGY|%bLJ0zy==h>FY{j z%^h0s;14Ibr3*!bII&?@zFjeeT_reSkjP_glSP=tdZtU8$CxCID)aXP(`Xu)AH~ha zitKls=!?gQdUBOzh=JFn5L-_>9Ts9kgQK_86X45S?u|CVTnm~}=IDrFK~{B#TfItd z1DeQ-ye1+RZ|>h;^`CRQ>U|qjC+0{%F`Qoq_lJix%SBJfv0`isdQI1evQv;Xf^%YiQj012*u)WPh|WcCL?kZGysua72(vC0~a-T>2!?Q1^1jj+Ud`h{^lT5e=f8c+G&SW!~3>^_+JVGi(?0d05IM z67yVnJgtFLp0*PsWX*@xO_7Tf_5x)93j%`#Cr7WQd&Ou)qNs7C)E{vr-@8n011w9^ z=hKAAgJn|8M+U(XuD1SU;T6}zaf|{4+ue8_oJo!@S&J!C{d|qh?z5o3g;%HvUf&*_Yx z&ck^ia2lof;WYEDllNQH9yA47Uw!dVt?^WxRC7g*okM;;VYRQ(*e$D&oOvkm`R>Ct z-f-i2)>KS2&x3fu;x{+vsT|AF)TVq}t596QY+M~89#;rPJV!dPdA)ny=(+;JeY;M& z&MceX8V!*P8AIl5m`Vci1^x%a3t;RXdZMnu9pR4soVC*=GfZUAs^+nVD9Gd!Or9A-NfQ zV;$3|&03-uh1*(n@fdzGYhm?_acSB%f1h$va6Bj8jOzjlYF}ZKC7!$}QZfQtDnIhY zmAd>X`{9n!D(G@pcyA`Lg45t5s$D7HrMBE8S-n%iakVq)ZY!2v4Y7GI@5$KK@%laO z!ya(F>h5}s0(5k=ptkgXK?~D*@{Qe z;RPt*J$PYk^CK%4#6(35IJuUztj}?LCR4$;WO^+|wfy4#XYi36S-IKiICO=f6Hf{8 zwTZS`t9+p?)~E$9rsB8%wwLKrw-^g7}Cy)n?{GJ$@=((7ncpJ24kB=b#VjSn7i~35zgx;G)cp7b+EhBea{PAB`E@!s{+=(sL#7Ed z*GyFzRh0(F#ZNZ?Q`fJtw7=#-#@;<$+gw|R=KV!%}7poE?c ziL$YTNn*3uaC*|qNw5)g8p+FXPd|p+>vnLX+Uu?l5$!0T>QS1%DfJN{mS~2t(5bT^% zUH{t0wn-F-cDSxcM7_6I=47h!VvxZXvJHDEX`)Bp z>;gDk&s`iayg%Nud@CCBKz-fFKR2MbbCaBJe#qN6yCyJ9VQRM75SrT5$^|mOb@B?C zVy3QeW(-I{^LZzCboxQN#S?_9?F0#8XH^;e=(U$!JTTSOi z2XtIP!r9XeuSw1eH=8EXx_9|I8T}Wu1-KCFzP9S0ZcQTU&YoXimT$DwpKjP6Mb7%R ziWA?5uXJySb&x~#>UKUq+|f4@Z+Wny3?emx*?Rk1vYe`az3H5)U}2;;6?g}({f3pi2XsxLIda)|NFO` z@jsGeJ43+MBtt+m@J~u#Qv*XQ2U7=EZ9wxZV9gR>LARZulc}LI;BDtO#u*M+AliHG z<{6S7t8VddT(<&5z6C;m1C{G=7}QKyl9QLLm$Ux*g-hcpYM>^Sb-J;xS39*8 zr0-Rv(m*`HQAF7($$krmx7Q=uhsd`J*WtrLa05LF96lkT#QId<+-sknePx%*F0S$L zfb_kdD`Y_6zP|^`>vIy2f#;3F2U7?NIEwa~YvQ*VLv>W4`mn}@=2URu>8-HaWIj1poFj0;7B)SJpi zwd7ke%+F^!$601eavZ3iQ}D7$o2>)H>tCf#@*K=DmJI{3zh76tooEfcFUQBs(G8q- z&5|&k*M!KYDQ8$$*gItS^NK8?A17b{0hstN8{Gd{0QOUs^#BAC<_6ZzRzJ3a{dpxG zW0e5mg$A%xyFwOkQ?FXLY(oqM!AnkOAgV=^IfeY7j}QZJ9!bxgO1ERzU^u{qM4Hg^ z78Yf-gL68(d^fW>3%_I&pi8B51vAU|RI(x9jw>o}P0IQKERlDRc!M8CTtaBSD8$k1 zwR$AVk+W6W-r&?rT$}4~^nxq8 z3w}1SGxAC|;D$F3dNq*Hq3dBB^NS!~k*=Q~dJg4lUxYJI6uIIS;|N8Uyq#$*=RjG* zn%ZT`EBsJ5fF`&vk+`5?lxT~KjoW6?c+llXTOg8Qa@D_o(+5$IW5ey~5wi~ji2w!4 zQ?1O(R@&>Kji6U4(^~{jT})Lt>}n%Wt$bSyd%ug!8mb^Eo;d;%XoK$#Tr0c3fNzNK z4o`nTBKUZl`?GdJUA|dkkDU8sYe;K8Pxbsz6=W&9Df}QYe$xbs3 zz7jfAqAT9MoGKn!2H}hl+R8omxQkL%GOO@Jt~izcQGjtDBeIXmT!;#c1fysqnA9jg z+{lZ+x1iuy)wz4jF{2ouuz2%}SXSTxH6W;nH(}YI>!nly>S%9f) z)p2Lxu~@E;B}$R>n&i+}$A1q5ZHhXGSv3yGUki77(6QLyl2M-8Jgl^(U4vD>EUvnI zz@BL-UoVI_Qo4^>pOUnEY)Qc)jh~jdV*vDC7FIsJ5~C#2U8^to~aquMRX>!s(;m7LO1Iu3kbvs>YTJCBHJ+tJ7t{OqG)qU5=H;U{6 z&wZ4b&fwHu#JyBfrNSnVh(bF~Nu?4Oj_)sR}UsUm0LH%pCT2bNsSj-T_R&4?Y-#D3J34x!l3e$)}h$eS| zkLD?+x_kF0VB>r=c=7x4S!CkFCr};i5k7pTO1i8LPkP9^yYKmksd@rTWH@K^z37vb zbkc^@Jql^mG~NV|<(m6QC%NWgSPC7%_)nDKE?B^;D8lggT4V zQ7A$}d7X@ev)%(bcTF=ottE7PS4{8>)x7JMFoEW3#17b0pX8TY63&?6h{8A^#71)A zLX-yI5c(-8(DKvmjt0z|%s)m*4d*g{?Hr(bmko89$vAx~h;PF{r@7ob+Uk{mxskZ0 zc1xZhzZiNs@w9Hr)~Z!?`JsNA#KG(xNN!8RW8HL`?YrQbLnoQbXwX;%9B;t1bp_jID~$UPV^noE>;+im_DR_X%afrwhyPB@cZnY_lxPNkH{ab}V+PEX=Bd z-xsQb6B9>wk)g)|4UZVLtimz7GR8k0HQ>FsDOPiTH8$uq5FZcJCfaOuMVIRANYZ$( zoF6^oimG=5-?-0uH=IV#IQ`jM<8yIN2hOFgc{$-kIZW#~sm2pv+(bKS9#qv8PR&bmv($PSQJvi&_TddI?_4D!T-H1A^McARx!qLEjkP5>>J*(zTg*;LuKt1o6QrI4dZ<$O%zZ^A#_+>fi_c%7nB);~5440mE{(rb>#78I zpS^duk#D0+A~uW@W0Q4@+8?`I!GHc(_P`*}CJKx1aWe|1yW`b?oJmHOs#?J9^(nPw zpjB~KyRczHXKerJapuhW{p6(54g1&eP^o>U7o0zpi52+7yay1P?fh~K^`DVD{9Bn! z4E4?b>VIQnCx5(^`!WW#ZMRJMrytr_K$iSw#1y|Sbu)f8#tl~95@>_6WIXmRhea|` zLj>bqTED$dXto#2%}@y0h-&TSBnciJ?qcu|6Vk6^3y&EWwX+AVhQ6K#mQ!U#bUlcg zpnq!&97y-CO> zxGg{P-wRP3eHO@mz}gZle}9)S@p=K{~auoU4cKmDDkhJ7aw3 zHYz9kmrOZ_CN5as9Ut&@X;}pCsh+}&Nx~0GKFvwfyJoI$%baW$4#3UO}BL_ z2&0VT`F1u%gFV#;w8m3rKJdA_{ClfBY>K`N7C<=jewn=eP2r(|p@Xieh5cW}(_o}> z%!daaWFy%#1csL9b>7=jS9jD6dI#i&rWvXaeSKA@aCd|yiNZ79+?BFeh~Rnik{t*dfH z)ybO~4THrq!^g>5&er{(`k8X;Mehx8WyQaYkpJ2A>`(ptbA=^fjlQ9U!GD9o;m?(p zOKiP_xsqzoLVfWznh5&#{*Uw?zQ{|NwKfu)U!wS)CvrNJSr^2b(B0rOkR_*mwH(rFd& zEwr76Fp{BFnr1BC9`6LC%4*C+mP^K6^`=iSx=<&0hwkr*%0ZcKx^3elku-AMunIe6 zyV;B?m*vl;RS=ST>sQ% z_^=}pXzo&VZFRQFDp@x~k`(v;XMRlb`NHf+iKq0Jc>J?Xl|Pl@zcJSV(hC1Yv-nrJ zh>dKQ>Y;<~+j}4{k)q~ru9m|&bfdL_DU=6dB$x8>le#@2^QN%+ikL*Tyng$<)h0|# zq?xneq0>aihNY6HSKTLDud?9hfd+*iV=So+GI8sI7@b~qs3EYI)j2zMwK>r)BzZtS zOb#iId?Jf;clrhPIdu<@Q|l`xSlGDhoKfe&sOqK4QDb^0d!@?YS7t7&*eJ14Hy?E$ zFA{FpbKK98->|@HC=|Ur3U!E>!{ER^Su$HK;e3ZwgylWy1MniztpO)iw80C5xAqYh zd+surK5cdmZE5V@V(#0k%v8QY3Q^u_P2wrZhg4Ill8S^90#vVDWkvG*0 zYX%f7vGPD;CSi<^#Y-vqp~U+F8Uxe-wBn=&RT>lVZ$czX7qh_lYAG~=;OeX@V2lkeL=Eld@h9Sd8wr1+R~5J52z`ZJv^m*^ z_2ci^!E{HP`UtNVO|t*~K;luj+p9AAEjYbiEq;s+_NN8lpm0X)HkP6*i%(_5*fD82 z>9|jJ-Q2hbQq3ygyI&%j;AVv?U1fKVpI5K)Yp_@qmDtX6t7Ce_NXVo`Udy-U#LJeQ zk~ZG-ctPORg{U*O$iVp|u3F9`rK_~`=kLLZuT`$XRqRQ54oZjG4U{b2BUO7_4T@rV z_;Rx7)N6eLu3!COKK{tc9EBZNO9O7W;Fl@8f7VL$-*4Gc*XFMp^|MDxjGBB%f?9lc zYieHEMqEPbT9OJ}jGAU(obsJ0DsnDr{bzL(S#83OLOwi3P*oA zuc-xqy%lg=U3a*9BPeWVt9n|dw*QNtKd{yz>8DPaPdiV)m!7?Db9s99nj>*hoUKqH6(i9f|Ts0@%5>sN_M}#su?@$ zgm3vY*O!t2tJ|T9`GNMVzW#^5`a`4|L;N!q0Kd?FDN_GzJoMiX|0rJk1=#<+I1P`8 z!u(5?29bZt5=D;Tw;a1T1=O6ZB61pX3P~E-f^S<_l9GcG3KKx`HV1wsTTdL6xf+p~ zazM(|Xn22~a2UJnK^JhsAAb7pee`d7NEzAbTK=~qY7GBtk?wj^}Qs2akZHn*;s8Ro+8O4T>5v>3ak$= za-GK>Hh|TsSO~ZWFFHA594ZxE20X*26k6>Pp8}5jF90m`AG`p+%6}{$1CuDg0CK`9u?G)XjBwgt7 zuKHq@p)heIowmlik>e}V=Caz#v->kP;D=ZQMi3)g`=Y&9_gUKT44V$)f&fy9_R;rF zI6${o^}C6n_D@2yp+IaOJm3=FQH2{Nr`e$PPf{5^sBmb? z+p6UkjPq63-tS3LtQG9IK3`n2BPhsr%`4dCLd=_?(r&r52CU0)XUR#NMHGZc0HbIi z^`Yz&0&RogOA9b`e&z_~1Aa>pWg@|&s9VXk$C@LnhN{qA&)NjwmUuW3;vihNSmVFd z)9Bv6UUX?onv6ELmOri4T{ce}KJkVC4^No7vVm4XB>Ep;LK@#$$DnnQljtK4lSl57qSiSRANG1h z-gTWpg<)cR;t;xV1;wVH`Feq2#v>oF1c5uWUBiLX0@|1gG}|Y(uxo?o&y_q9dr)m{ zY~eh-%vCamAPmNL4wrRcYoKrGJnW3KK&-K?>?zHNu^hj=YBwFAnZD;2%hQ>w`+V^B|RKN05ZrGG4PvjeEjb0Tn}W!NCuVf1#1h zvP!&k5+4y$0kw-(y^kUP5G5us=5ok*Uyt=|qfq?u#EEC-+d*d&=(cu#!2T-yoJ~u- zen%!53bsm~tdc#CBWwAHhMb5#ILP=|J_Z{?YH}6*i-`}5Xr=M!Cz|E1A5aN;_V#_>)9-0NRRqo-YUSJW91KV`d&T59G~ zYk@4r_Cewt>}t!(*X7k|)omcaaN(Ddi{h}7wq9tiv6A^nywRW@kRYF=x8poebk<@< zN8&mD3g1E9vk08%$Tz-GCpu>y35gc!fnFNM+0jd*g=&lKOIv@g9uX-@fXofco@Wh* za^w7TCowO?d=$3S?V;8i=OgyErHM)qq@&i0g8rX-IdMPRrx$cO8qU3Tg%xe zW8QSt1{$1o^z{OU284~G7H=kcTW?C)C~*hDjaV*DeHFB~^1U50JYm<&)PyTW_Zh=S zHZ>J%E$b8y0UkU@dU{Ux+&S8A-rA2k1Lq;9gpY@u-69oFWNxL&@s@h2hUex_)&Tqc zhhH0=om7$n)W8p zTOah!eS}f;ED7XuvtxvJ6j^xq^A{H1X~teM(BZ)$MYC67mW)#QQ zv#aCPouhMYYY%nF7d&5oKYtpzp)%vW9l6jL${a3mh{X_$)f?vwb$xGiykW>XwVk8* z%~gD;oL>wO0EY**(keg9HAoQdyikD)QJnOIn;DA@TK z6_d}1GnI!WRIgHkun9p|0xYt*cj@XnQ z-RlFFI$%W|r0yL(ZT^0i4k`T?lrh~S0hD+=&)06jcbL3Y^y@@dVMIGe*=JbeiNtv$ z72td{PH3bP?z*?j?={k$cped&P^)gK*8+H0y5^feA1}cnDp*NCoo`R8SA+N%w}3w# zG1(o9-Kmf5qC`jcean9_F!E|V7XL8ej)SMG?pF3~d$-8tvt(U3D!KurPUoFO5(T<8 z5k2uG>sk>}Clu8%Lv=?3k!eWXT}458NETiHT1bCF?l~n5!#SkvJLRM8;!-jyIT+CX z1tIIE<&9vJiLc9QF;4>a2%ww#?|Wfg62!ShIPuMl;|Jp368K$ia;C9Emn*O3VkisZ ze~+C((7J&*7x4$pnnuu2ku^lwcAB)X2Hkoe93duG|-=alsKoV~fO9zcHhrGUPn_X6w`a z$<9LA_ItjgQbpcgAU2s)W!8pNA*sC^cE0<}ewk)b3d{4koIpF4;J(wl)j_jwklA+X zBDeE8xSIoSIOF*0juFP}F(Q5M#G~XfU;(v{IdCF|{r%^vn<A?F$LmrZ74*q6|IkjZLGH0JE{ zsp3xiuPo*$S&OG!9uNiZ=LxV8Y}rZ;7s}(nR@yGHJ6gkJ0|)wKc8$?6L_G539QQoX zcP;k#X$+*2jY~wR2Pv_0li&LeOD9f37h3vJHW=&UiIjki+T*myFbzy}5u#60)pE_; zlsVA?7cM`pWT1N*E}R}{9x&NB!WugWCl^Kt_wmXg-BE|}CpVJszR?upgt{D^uvfl-DI8CdERJZ)VatGJ&?33|E<)kD0VFV)S9inI+|U++5UMC zzumwYp2z=Q#$t$4SPdyW(9ORuhyGF^kAu>SM=ehO=?!8Q50Y99oB^(pK9^4*lB6uEnzuR+kEhyG z4nFo2PH6A8&+8U;&Wj62KMa3jpGfs+{<*Jo?FSu&OWRX*& zyQ)M(vxFx02+15b*TQ98zm!m+X}+r);ErM_gL=2s{zW+7DDh-ts&%afrhDyfzWq)+ z0N8kjNhxExV^#fGxn_{B2rT2u#WFZ*m}G#>;xr&VG)cIknYCzS|AbIO&%M0@yX+(l z+qJ!U4@f(zpq>vB_L)pqRa7FFvh7&TD7j`!DI*gFe zoF1k9?9RKu6)%DzRG!vRPTVQ)|FgyBjE4Q5hRUWLn$!8_y}|UO9?& z{7@>GZ^JN(#=5`NY2`4yZKi*EdMJ5G1@BMKb^6I(2mxUM$oxW$X=g4)fGfOz(|O zEvJChx`4Gbvc{$>Wh2J`m36I_-uvnri5;x~JIAJv8XHsf@dKqqPfCU|>$!*wqR>sn zX$55m>a>X%O!~@?ue2{otyk2%5$K+eUQ4eGCSEn*_i2udnHuN%Ih>oc)!-T17RN)# z-u+^;?jIt@a2A>zlF^2C6`#c#8!VV(?;G{WVTxBdoRcN@JHEZGH9%u?E6agvo`2rn zqRMyix^;O>a{Kb!K&e&hn|-j3<^2k1Uj8EzLS=L#^#+u1*8n?#|2`7Z2SiT4p@jDr z>bV~|!2hV$gei<$A2CC7se6O&b;Ef5Oi%A&Rl^yCKVamI zdi9KaBK(qqDjkTIH*oqW5>k@u7EkOWeDe;D%x`gij6p&mI!jRsOC9w!s=5$fR1I^D ziQ8`XMljgJ&LZUOGS@*{gc~hz9r75N+!Sh;sfu2GNuNuVVDcHHp9JVS7{e`q(QiWQQVg0ZYB>R;JW-DbR|~m`9Aptg^RXoY0Y6o5`x|xlY8yYV2-Bbyr|I8HnHl4n)Y;nZynOovI-ouS@>p70rzQl#lCoM(tAjkEMjpN zj|W@5kvFZ*T>GIPdkL9PyzA6LfEbI!+-C3~rdSU;V9g=|oooyHV%RoBO!VqLLw|FI z*An6~32`%`ORC#h>@iq8oxD!?Sdj<0^>Fz0mVgZ^%CEE`skiQ(0Ig-P&czL<-y1jj z5~oT0h!hI+I3hn?@x25_{v*`r-CSDxD5AkpqfVTGCZS?dmU82>qkg!}>*IcoqL0P| zqGOgyn(=cATU0M_e=5#iRC?@>B1}G@^7;3Q^PAe+f2lZsz9v%THO-glkUitQdIjM+ zDGIA2Som@IED~9g!xa3(;lqs9iPdn(V|FIwF4hAroYlG1RJ3X89L5{j_e9iH*M?Di z1g%j{BIRlqF^}-G{Jzpi=3bdyO=FCb>Ap`>cIzc0wlM6{{{E$K)#TQeD2@LJV>kB2 z!~p^*ZG3trcJy6sMmED2YTIxt^P280CF>kVb5-xQBuPRzG)`wKRu0*$B7GtvWjdFn zvtUHKmiUeh4XnXw5Ok~nY5dhzah=K*aBVQnoMKCsH}*!p2}OrGenI`zBpnwuBE~Vx zM-~*2QLDiOIH9(Hf9_ofE*5L7*Wt$LFx!RP#n_NCs$B8=ShOL}kIkJrCgx;rVx!kO zFpG`j_<78IW$`QSnwBG^T6ba{YzopG|s}x+#uwRC%HE!*ra%-`?J|O~K>_^YDIye78{zW^H&7H%U#gm73A@k_T1J zi*_1sn{LBz5}Wjh?Q-nQ?mdOclS5%PG_vW;J2>X!fdzC6R%CoI6#< z;tgJ69cV}Gt5!n={`9k@q%HKxsRK~n!}uj>-;amvZ#KvOF)#&K9PrmVjR%0As7nT3 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�%4pj{0OO6%@;TSx!rNAjjI5xIX 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

Kz;U;j>cAs-6!Pbfq-f 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 E1JtC$CjbBd literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.ziphash b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.ziphash new file mode 100644 index 0000000..1e3ca7a --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/getyoti/yoti-go-sdk/v3/@v/v3.14.0.ziphash @@ -0,0 +1 @@ +h1:cMFC/PuN6kuxMfPwX4OkVyQDA5ZousWnVMfUhIhKZa0= \ No newline at end of file diff --git a/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/list b/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/list new file mode 100644 index 0000000..12aa8c5 --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/list @@ -0,0 +1 @@ +v0.5.5 diff --git a/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.lock b/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.lock new file mode 100644 index 0000000..e69de29 diff --git a/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.mod b/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.mod new file mode 100644 index 0000000..5391dee --- /dev/null +++ b/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.mod @@ -0,0 +1,5 @@ +module github.com/google/go-cmp + +go 1.8 + +require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 diff --git a/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.zip b/root/pkg/mod/cache/download/github.com/google/go-cmp/@v/v0.5.5.zip new file mode 100644 index 0000000000000000000000000000000000000000..40a85957ca3d3a851c81207d641916d8d0d10aae GIT binary patch literal 130974 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%0AMMJR>Knzsx5%F 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.ziphash b/root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.ziphash new file mode 100644 index 0000000..e0c99fb --- /dev/null +++ b/root/pkg/mod/cache/download/gotest.tools/v3/@v/v3.3.0.ziphash @@ -0,0 +1 @@ +h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= \ No newline at end of file diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk/v3@v3.14.0 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk/v3@v3.14.0 new file mode 100644 index 0000000..79ba94e --- /dev/null +++ b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk/v3@v3.14.0 @@ -0,0 +1,9 @@ +38859053 +github.com/getyoti/yoti-go-sdk/v3 v3.14.0 h1:cMFC/PuN6kuxMfPwX4OkVyQDA5ZousWnVMfUhIhKZa0= +github.com/getyoti/yoti-go-sdk/v3 v3.14.0/go.mod h1:FH8g7mRttc6SBUd9P0Jihm7ut0rNhkU3rDFljUHL33I= + +go.sum database tree +46687137 +AThGHJvQbj2AItv5E753W1Rtkno5LM7juWi9QkFPmOo= + +— sum.golang.org Az3grjiLRqz6DECL67zEs0IdbJ0k1TjmcbMkUj4pjwIgQqQ5i/JXxTDISHmldTEpiNheRMxEKdlomajjGk0nkaSZCAE= diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v1.1.0 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v1.1.0 new file mode 100644 index 0000000..c0d1702 --- /dev/null +++ b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v1.1.0 @@ -0,0 +1,9 @@ +261090 +github.com/getyoti/yoti-go-sdk v1.1.0 h1:gL8+9+EJc+4rg0RbChTyYzFzSK9bmLRAvylZ0ZwAyzU= +github.com/getyoti/yoti-go-sdk v1.1.0/go.mod h1:q+DK9jO6Ir6nyXmIYH6wwrWq47ibBqK62fhgLe4kqzo= + +go.sum database tree +46687924 +oqyj00KFhQ1r/LMLYjS+N2ZcyMBmuy4E20Qx5wubH18= + +— sum.golang.org Az3grskfHYkRAlx6xDdD0Ld7PWz6CLORxgtPBxvd73HO8lvyC+Z2yYTTobhK5XRat+vJH6yqO8Oqxa4++MKlzqCl/QY= diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible new file mode 100644 index 0000000..2a14c9b --- /dev/null +++ b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible @@ -0,0 +1,9 @@ +261089 +github.com/getyoti/yoti-go-sdk v2.3.1+incompatible h1:+hiLmgo7aT1q1psL5ypahU4jHeUfWnIjF9t4CgJQSes= +github.com/getyoti/yoti-go-sdk v2.3.1+incompatible/go.mod h1:q+DK9jO6Ir6nyXmIYH6wwrWq47ibBqK62fhgLe4kqzo= + +go.sum database tree +46687779 +pxNpO4dXPoJtrPRtCMq8KxkiSi5F3cX8pifl+r4WP7U= + +— sum.golang.org Az3grs3KohpAvBeFkfu4CQTg0VEAMa69PaRcHfZfZyjG6f3qU3VgYStt+C9zFqWMegNhXyhsQCqGznH7Utnk5n4rGAM= diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/google/go-cmp@v0.5.5 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/google/go-cmp@v0.5.5 new file mode 100644 index 0000000..cb9afd3 --- /dev/null +++ b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/google/go-cmp@v0.5.5 @@ -0,0 +1,9 @@ +3171667 +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= + +go.sum database tree +46686987 +Z8Xci6st06/WRnp3Fiiyllutio2B545XhO148PaLqn8= + +— sum.golang.org Az3grgqT8XzQNWm0rf0clpbxNQUse9V9F5TeZTfBJlNcEFg3FHwPTA97p6A2JSjDFmptr6XJTtPx/EgYK8eYyP46EAo= diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/joho/godotenv@v1.5.1 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/joho/godotenv@v1.5.1 new file mode 100644 index 0000000..2e12426 --- /dev/null +++ b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/github.com/joho/godotenv@v1.5.1 @@ -0,0 +1,9 @@ +15449903 +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= + +go.sum database tree +46684670 +/AOfVNHvKwrfPFvkTxwDG7szue3hZXrsXDNPQN3oFdA= + +— sum.golang.org Az3grrWTXFuqO1ViDo8WBY4UXIneoik9d5BGNcm5xN8W1I1tP8GizQ0lNQVfztfznkxvfVF0cI4emlQvI6Uqim3Y5AQ= diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gotest.tools/v3@v3.3.0 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gotest.tools/v3@v3.3.0 new file mode 100644 index 0000000..111c3b7 --- /dev/null +++ b/root/pkg/mod/cache/download/sumdb/sum.golang.org/lookup/gotest.tools/v3@v3.3.0 @@ -0,0 +1,9 @@ +11035727 +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= + +go.sum database tree +46684715 +dtVIxIDuMQ5vISP9oB3wTEU0L4aDEKzeEtAlFI2eR1I= + +— sum.golang.org Az3grg+g4o0B7qWCziJOy+0L8wM2Z1ijdrokO2RnAiJrMO/5ccjMwd+E3VrnEgsmNWnmNr0o+lGylSp9ySIqtwOTFws= diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x001/019 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x001/019 new file mode 100644 index 0000000000000000000000000000000000000000..1d607c2f7c20136fb18a611b04fadaa5c09866e6 GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x012/389 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x012/389 new file mode 100644 index 0000000000000000000000000000000000000000..821ac729f9f465906e33df85258f16f37038a9fb GIT binary patch literal 8192 zcmV+bAphSV$SL^G@K)>?YS3Vw0QV8JaiPvx)@rD5Ldww?b>K%L!VqApNj`3}*v!Gi z((Iw%zWSmU@0uMc{hyQP7H@(;(&ZP|>`)`AU8xd`GNRI{V;RPm#)4h=WDtixwl8Qb0r9=Etvo!8~DJdgo%eAY8 zKN7tniDdyCl`kUBI{HxgfoqO@Qcqty!pLU_$u0=U(Bt?VKA~`Rtjc6&3_`vz&QScc zM3E<;P;a5GTewpdn8~cj@W2RQaJu76!c? zZo~PRqXzvKJRPqb^wiom`h5EguAYxNcvR>y(=&B$%^d*T{G+6dSAKaLPQ{0&Y27NTTSUd?yw7)!MN;GPZ4qY?`o3eX(q(dgKcZwS9 z;eA$Ee(Eh5-Uc2f`Xy|{rf%BcLgS;uL+KykOcz|^&5EabL2#F?xY=M;#qH!^&meTxLK zr!s&d{|oD8R9h=mVP|(N7xQ zKWxwXoQ|8-I?IBj7k|O@-63ii8$YhgH~obn%!m39L(<;%}Y}T5saR@&WkvIKpf@H-|6<{c#kJ z6^7vy@I~?|*^KaFdOlb!?x*C53MPrbNBN>j|v zRJZ4kDM)&jVnfW5>9eylh@lSf*lm(NmOyQ_R zNARfIu&LG3nEhOluzQS2Q?;X&IN=?8U+#m#5xcg&M^`B=Geg~AyiK!ZH?VBTp!a}! zBTaMvzGPsgLMo|$3|?(^{?bV|VZGtD$lW6U`O%Y4C}W0u3%_QAP)o~6rI!fSe_&9n zD5LO0LD&?a+PI9S1eCZ(7q%3vtX4jt#8Kg#p4)EMY@ zzTJM*&;>|4-f0%ymL2_4p~8-Mfp7H38)%G$GBOs1RmG$3caAVsuP!Cfl9M*QK^0CB zsL#Zi-I55ANEpMcIqcWLBY6{_FMAnDFY3WwnN!ehOO6;33J1X%k3^DT3B*UGU`e06 zHQ8R!U?1FfipT3#sj*{75lShFIbz>0=Mwvm%ZJY6eq5Bxw93Er;63kmU* z{I>wcD^mVr`E_U1;2_pmWhJ}FRTn;UyarYWZvNKy(3&ubn7V294`?<|C@HO*!A7R& zpZXj$eGFU(1mx)rDlnq7R^ObCpAj5r9F45k@w&!`pNS_!sWE5I7GhLGEN!$?)Q%${)Dp~IX*wuS0 zLe6bKjJ#J1SzPA0z`_5NwBd_X-Z6FHjOzo7#yVHH>|Ej&D@9O`J#^9rg~L#;Kjs6e zncOV!0+2rIgEmjq>;Y#u90?iJ9Z-|jZgQ%d}0SYWi093NGI z8x0ziR6!}VLj3z^!?SU=U&^=3q(bfdJWj%yJ}NDLE%V!6(x54nYLCLl(f7(2TEMDa zHNu#u8mP!jZSbSW1^fvZYO#Q=PCy68$K|pY;G)(yv~Jg)(1i0*E3X%Vy}Q9rL9p4a zKwAkRvI4XlbB-)xlBdZ@ee@coNDu z@5tB^4|!T{X1p_540=MpgKr94(^jJ3$Hr*EK53d6EE;X=WT_>)x+_($XUrnj2mbe5U}EA>+#ibkr#r-aHr4 z6mxBQgOlvj#t8SMS*otZks~PK1DlK5&NCCB3Pp0Qs7^p| zMh`18(~cI~iwzfBAZioIMv5G+zVA!^+Kn0 zxE_W%pydrpNfpX6GCKS*Ec=V?9d?Qvr(iC9XPZiYv(uX3Gz3vYrq7_}Rw-$P2E~o; z7JkVS%|XsAy5hB+kt(tG)%KZmMQJFlDNm3~Xf|9Zuksd_D6Lg8qAJa<->vqA2IEhD)xa`{hovn-LJrlaMO=Q}3@ap=U&z zwW6%Iov3I%PZvQ83*u&R9W##`gSPVVN!>{$jP!~}@692GmBVu60Cntw3N`*D!!({TR1ipf>VBAf z;b91Rok;ceh7#JaHyjj^69Zv`y?(JrDk*EGoIt+?_{nh`t@juSsa-aRK3L#VV>A1b zTFAGFW)_qK4&&?~u3^lNnbA4hZ**JK(BH?FITYBPezlX?oc$BGtgN8Jfx~_o(CB0} z0wp~}KZC#ANda1uyX;0q?CI5958c-JM!&nv

ZLQDVd?)0q|k`0vKVY$P$Ft332 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

1D2Ud|NcVpD{y53A z-%conDMZy`Vm}Dd)K=wH2h1tl{`Zzb=dBXHl%_bJ%!aLD{YKXh2bl>`=BFOfC||4d zPAIM2!J0eCpj#nitH#~yDP5se#D*V8csttRineN8s12Y{)UFR8RW#V?K0?f*33~

8a`|(^gxK{|}JLvk5a2%kY-ZGCTQYv7PYN1IMR)2#tgMs+R&DnnhbR8JR z&lwKAhc>+Ro{uJt$$y!{nb`HvM8K`t8F|TY_ppiIYGG^kp@vPgZb2F+F^+3mOPyifDjMgaq#~m}2<&HGsDx8W^lQKJzCDt7{nY}ySuJ6|VYfB_ m@XM^K;2_4e`=Ga(pz-g-itKALPe~D4X1QDllES4Ko~VO+_6A4* literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x043/108 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x043/108 new file mode 100644 index 0000000000000000000000000000000000000000..f248aded5ec041a800c48bd11b3f1d258204f8ea GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x151/793 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x151/793 new file mode 100644 index 0000000000000000000000000000000000000000..94d1254ee64cfba93186772501ab42c7baac712e GIT binary patch literal 8192 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�ymHM>;@e^R6($%uXyQFf&} z&55VjaPUXz^=h-+K#UG zUev?%q40vSVfCBcrTdjFkbvp5q>}a5@QwyoYjiuABbsmnaJejV1vWRp5=?ctN&RgPV4J zKQ;X)vU#GQm)=jJPeQXBue`09`wIg$KM;>VBu4NKFj=eKC zPTRB!N{Ke@4(~={a6h4B4C801wa3tT`u{S=NIYbsrJSs@nrpC6(I500Pu|TU>SkCz}bqOCb_3TEn$}^Vj4b6P7KSHpdK7g>Tb*{^cw>BW%N6%k3uv~)zWSdEdyFSlGOU(*uk0@*L`M!NQ_z_oYB z3ZWkb`E->gIDr5l)g^NmQ}eKLi7#g)0$10sNBgB?4efRs{_l{I%b8bi`~P%WK2i*h zW+-CWxN}Jj=RFNMb7hTxuR7CbQ7$%b%F6U@R2Zb-Y_g4S0DV$fsQ%=|+D literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/361 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/361 new file mode 100644 index 0000000000000000000000000000000000000000..c8d85bc4b7fbfa98bfac7ea0ed8ba01af03c345d GIT binary patch literal 8192 zcmV+bAphTw#m&yANG;Y2fCKoTu&_*bTrslBAk}nSf1XL^3|$j!cZoY?728lzqtSrc zpUPTq67{CfruS;N@Vj9}p24S^V_++1Auzy!a2{n+8b<0YK$M9oN0wKW4Yj3}oGueA za)E+RCsFiNXGDRAx#aw3(+|w>@bbj2ehs3>*yn29w?9C7WM0V>-!#G-)ZaA_O8}5#`&-V1TxsZarQ_^HrECFdBW?`}KUJo?9b)K3SP2zQ|0#jyi z=I%=wJy4Obs}*yPA}cYv8^iw(pSNc#z1H>=tg38wjBLQ0VQp^Oh~~k$DRB*DdVJ+$ zKt2d+i2r&Doo>9A_40p9;Ic)`k_3{11)*5)9Vf4X(Yi7$8ttTJz__rL@!cYTbtBt<(UF)GAx2~`%l$%ps} z_yrq1hJGf;B`a^qm61hPLNbYv$1-H#ego#+al+Keh*K2vss&d~{RP-(446Ai5CxFI zAQ|jVWyvTo;$tsu98o1N1J4{c>oAfT^p&&LU&bn;{JZr!-L{!NNUWTF)S=RkHK9ZlksAVWp0 zu7Ro13luRhqOpv+dXQu>!rA!i3T7Ivv`vV}SYwO2btze8%e5xri@8{&paU}3fgTC4 zQ(WnI!;b?>OQ63GpXLOXI8RN&{Q;~I2rP>8cBSm9*Eq3I-H2H2^0ZrY(gK{Ce~DC1 zw@1ayr7DTm@g8Bfm)Wj0y2yz7a^VXfKa8>C<*2W6UHNX#R2)wC6PpJw=cuUGG+Xn7 zw0dlebYd3L6tf6PKcDi@NJE0E?tpnKzML(iT{v2;=QJ~t#a8b>(2(GVi*XPNxTw6= z8tVG3(Kz4tz$D%W6{X7I|2Y*Tx@Kj5#~kg&?g^^2G{8Tm(LPij%V~gKzs|&M(}`yl zuu);xr)iC-=1Ws_nH`T?PSSkwIY8Kt89#irj|bunaOOrHTk}-#-TlQ7MQElM&5(k% z*v(c|=OAbwLG`_Nufd%}KC*qUOXB>yMAJ#XFNVqBsvHt-uK#vBG=4)V)*B)5z~@dk zT7IJ*LEjJgJA2c@_)OYNnf?M2>7EbAP$g?COo%Ws1un~x5Yy^Bm#1ln z*fjEzmdq5W`sGzyF=H<@Mf%m!4li%;RZsH=(S^uj4HvqP1OO6-7NMS;@;Q z4pXVv04@)%jXvR$tksqGs*t?{p;Hk6m zJicWjyw?ZCXfnFPgNNR+m?Jn)obH_(QD`cXT3CKNK$noC5TZD{V=(ao^Im&n(tv^HZN3NTDvV` z7z|QPOCb6=d~yi1w8F)O`Ou@T({oal@;)O0w52250;=4&hSeb|#SbOUMN!g7Q~q*Lw{lY`rw7--A3MMv{+VN2CiL>P|lJqVMUf z2aXMeh+GUn`{AH&Di-ilY^fhQ-7 z|DGNJ+sabIE}G&CJ!rPk{TdT+GA@vvHcz8@xO&7N$)G>qcs2bjzKJ zF*@@DXKel*sG&I7Ko1_L91l}+B*k7j2NTuV8>S%LHXc1Q^IE?T)nGZnH1>GSzYdL6 zg%+o5sl$L_i|vGY%Xi+Ia?HCEc`7^8kgA;Ji!b{|_NTY;ASZ@>pQ={G1^ka3zD9*; zAjqN6w;zN2uQ^u4rthFeeAcT;NS03*ujqbAGA!@r>SD+0sumxj&?5|@OMLlL zSEJ!WF^K>6k9gCnS!+6djnQ#p@pOK3f5?DzlaTa^Y!*o(PfRzL7gppfJRT>$b0l^S z?D5a9wvYW`vOrhgSsMss8>qvxmDIZ}1m=1V(YcjS=M{7{A?W}--uAta=RIDrI&_H$ zVsAPlOFbm1S#H|-&(7+|4BdU~Ea!(By$L)3->K`!MPrdCsLJVvZy+Gj*QXaGTkj#7 zYDN+g8$@oW9&)V0>a9;Gp$#GfJQ7kv5#6&?wbA$v7E77&BA=T+Z)iEzYQcZY)O39&p?hw~7CpY)d*)H8Vshfs1I?#V-Wcs1^U z#m^I71+E_i@Qd5n3)=DcnTXQEIDB}?Nicd^*L6n!Vrbi}n|j{IzQKBaf=w=zorH6^ zLU%|*6;5lR#&Sv&I|bU9X)349_*a`gft_$-8GURlX?^QgU)*^~JaM3FH?T~LuJnelp51RCdY7^ zDm^OX9)H{p;DB3Ki}=2Q;WjF1aISf-+d>jcHCpMzzkVLw6r-a3U45h4tk zc_A6-)k9uSoH@LE;7owR(8>?4+I}A$5aM7QQt2{WmNtjnH25(~}?Kgjs27am;^6J+*{8_BRLMo=rbU|iX z79zime&UOoUbmorrT)RY1B66i>lOEENJJ&?;^jrnh`Uc;U@hk#hb~AQW7h!?rI@rF zvq&{KYT??NG~J}htwkSHr@n=!FT9cNvxwtVPWSDNZzdN?LzLwMJ9XT;5h|Dl7x+~6 zJYd}1=YTTg;#CQo^W5X`|0~qw`WJF>@(WB7g~WAu>g#Zy-6Be=1koN8^A_~WiPNJd zZ=!<2#RijsOQ%^Zjmr!49b{IJPL1?ETo&CoQ)A7uMNHRI$~ED4Rf8yskHwN5$RI+l z8p%Yc?(sGL44U2(A>ZgAvT^h!FF=~Wa=Eh4)GSX*9pqBL;5vUP*4*$t-FjjICX#u3Kr~A)%EG*CwN&QoETFbmx*^%yAHb%#8`9>MlaIz&>ZTh^a3qJ8ux z_JoKB#X(-DZ2prB*UolpIC$-quz!QvWOE{&C54YFW*m{9BDQftu?JpYXyw;HcY)r2 zI7)an0#F(zgS(ih3-6qE-OHg(U?k*92oFSV7HNzD-rjd6WXzj@OS4xs!-W14Euem) z$#+J`VAo*^dxUE{yhLZ*Nj_fX13GYe@GZTT1jovj?=3%*l%Jx2HhV>>R?30r=mHiR z=oox5%w~MDF(nEL~m=rFdnCqA|G8>AT7=TNzM;S>8w?;uPt=0vISG0-xob$e>qbF@XwPe8;J zv`C11On`)6X$tHl5Aj*1WSCDtB8)<$*a7|PO>&@b(Sc06jAC8a_q6G~9l!&ueDmoHwu+J^Cy-d=R8#>5Ppubq4epq!GgfR$2Eb(A3#-%U$FIcS!{fGR1o5$@!@k4Y* z>gfY}tZQ4bT>4`)S-HC=zSIHD;-^3PPiY>JF=ut*gj&cYH?tFpW;Q|Rt50MD^g7Fl zdeEUu8L$u~e2a-QN6~L(qU&R`3;@Dd_T;PCQi(&`Ob`XmZXmM>hgQjN!{FyfvJLu6 zP?Bqe(m@j8V3?r&6h3+R>w>Xu#+&VPX|Gd$?$iVpGOzf5u`P1wep?jeLtTVv8ex}|LX_o_$Ta|RX9rnDQs~@%zFAc#`{J_O1KH=;n zKUBsn|DgSW+U_SxPhG?C1e-acKrUG61YOd2T>i_f`8k?-&fF(5RKA>&9D`Eyx{GuX zmRZKuv~#+!eL*4ckTroxa9RMcm%Z)qVQWDDh#%68?iyD+N}F1BHi`A%JBcQmo?l%l z8J{41)m|kMJ=lE>FqB#b76|doO*6ORr+@0v9Jbnr=X=+x4?@+O{eVk9p`*J%| zgDi-nP@u&`Zv5_Q72@d^gP)P{eNYWVFZL19PtsKyvpd4&pIy8i1HQDDXe@WoYrX;^ z;15mg?wvH1@x*XLXw#Nyl+9;I9NITuY$yySnE|?0tZd!l)3PA@>l}ME?sJ!Jk7D)w+xg@;`O7fL$%WTgVorwMQ_~ONAt0E&Jcs2JK-Rw-h8%9 z@&C@U5&GW!ho57S-W_e(_}$yq+lU8OAmIk|e!}m9hshbIV4K`-QcT4tg`u?&!WKSl z@#F&^BEvyAo~>V&x=RbyHT9{tV$OB?YLaOqLvhMFnK;+iGPcDui{pKJ-mX3PcxbLsbKcuiVuht>WDvF9VG)w*Y6HRI;q%jwOLMD z*Hb9~H<%>9Xlo00jR-egbmOlvpZLKjyT4Xtyv8di>b-#qujcPE&Vb$vevuS{Eo#)! zC)lmMum_^9+$`{yl6ab{IOi$c#62|*t9uM;PuX_Cl<;_LjON9R3LC)ylld(b45`6> zsgIjpwR0^*T2T4ED01x=Pn0?=H3YoYS~8b{a_#q};W`Q}qXYOVNb0OG^ql6K~l_14jThw`OMxUHa|+YJm;v8NpG2c`3&tHo)uFQaPQ zc&7px$a?W&Yb-?xk}XWipvXQCrnlZ$#E^qA9X55N8rA8sH;Gz$fKI>07!o20?fwya zufvlaQN(h+qbo`WrepcW!1$668UYKq*eZOl84bsX6L35u>SuQvk-C^!Lx-;4171tb zxk2`{&!(sLz*9eL4|Zm{o#Q}l5C!Zu$G1rgzUUKI>BCj_D@Hfg_HwK2Il4wc-X)F7 z#%ZfSPnxk)O}28Gzcvhk!&1qQSDtI`R4gjbu+Q&tV$W2Bte)eTYLn~!L`Q$>KzLf6 zd)@bS3DeGR)@d3%0P}i%(``WvbBEy7-4d2=k7%uf=LCzVsa(EyyBB` zHSTCYB(>3vQPInnyV@KJr0PUb8=} z@zDKLeGtgWaIhW8&&_Ww%T%bnFh{V%(DH6=tlnZ-?J>sWmv3$}JZ z*#P$C2Cb~aRx}2kwR9z^a;n;fkWTjRSFF+Y!n@pc)3qm)3b=I(MBzzT8<(!r0N+5f z#6FHboR1?(VSX)|LdqYM`f~G7eJPK6;Q~Bstrk{#{KGeH?nfmI3TigqR{&|4$u|0YJOS;#V47!{qNt5!I&%_bFipq zpT2&~cq#J)y}{14?`H0V^1W~w)CO@(^JO)6$y?j&CZO?xd!m`i4w#p6Xga<2bPQxP zx?^B@u!*SPbmP51^jCnIhL;#*0fr_hbFn(3Qt4pQc7)gscyNIP%i&>BU%0fIcbM(G z9f5dPkM*7OBs%xg7Sx#i`4qHk!X$CfuNRY!=Cm)H6C^pp57w+sH?$aVXu-j=6!zx0 zojCOOgK^4&sxr5&Nj+Vg zq1Cd34G&Z3^m@~IV3R2#xT3Zqim=Tn$i*sVBcZO)Oal2lG^TY zpWXlL4taxWF`n6E!;B~YCGFgWbK?WV#ex7&Sh*WL1zBFRv?$Fycx$-nK|`V_o-`%T zl8{7vJ6mvW)(Js}LG{`RlFX08pf-n+2NG?5W79)S;+ox~GBs!kp}QTDm)nN(YN$Nb z@WJN33%|ya$mVQwG(W&qss@#^07Fj?7`rL7z&^Ufn)$eH0P^>W(E@GQoAh2otYMu$ znM*jaIoT2N9E#x(#cRBqM5U`6Rz~5z_O^m@77a;Ukm$>}V)OR8-c;_= z<1B=M=OP*$uXoN!!(>8=09 zZF~_c_}tC%gbdSE5v9K#aT?iKRjmBU9V*b~YOq|C`l9VUUkC)vyzgW#|M8dJ-yj8f z7e%KWv97iG0}AiMhMToBy}QnhjFg)8oQfm@fSAZL*ibCgL06{T)p3vHWFcm#O_QVO zfMxRn;EA;c(N75@F-PK3-ky(dwTB!{^3YBQ7r)n{xvJ(JrxCAF#p&+;5xfKKIuTdu z>k%rafD6xtEd1LQCKDT9XhbrpdTvq0b<-0{a<0)5>O*5PxclH&=HFAwX8Z6SPzrRM z37F0)Iw>$^s+Uuz;baa$0RJz-J^j)Pdf+!PkM0&+4s1_f=MzxZAcCm#My~$ftxBv7 zn-0IyzQUjft?Rn=#b|gxcbjRp5BIp44hf7!GRX7a*D?m1TJ6bmg~>OK)Egp5Z*3K1 zrt0UFue&@*pjZ$Of(s$fRTOU+#I^wEqAY>WIX|CWAiQ0uA($6KN0Tio_fAc$^ewe= z`1axHI{h%nuM^rHh2&yo5upap6GUzkaWtu(nJUzaYB8I+fjhT7kjg!C?oTaqqsCZb z9l++i+|1xi9jow9JK4&E=cD6^@raTMXJNLW7#wABcZw1u1HRzR(PM<8yjsRKUS?yK zJ>So1?9D6LvJ6vTyStz`Cw0h%4C|B%|H->94P$BaK&KF z154|Zg7=E-XT-ZH4>&rR`9Pk3%Z%csugAiuA#?wH1?~w77q>qY=X||2!zs}iK~+rF zg2y?uEksJBc(P`K^R3M`v6$=`aNv0HV9HuA>w6w7r+ek6AP{xf`ko%+dI^R%(*n!& zzA6sd`?>Qt`%XdNhtKuXm(CHf-!qP9VcNDoFn>A(gMHDfIsU~CF3oMz6nM&0civIo zpCuG7`bcx&n3_JKO6rq0!eE_9J*XA1bHq`&%iv6$c_NBHF`-QEIB~62xQ(tF|`o@KEI~g zsQH<+_zkIyal`4oV#)|iZ>BT%lVKE;w&U+VVk1Bc_#>~g0k@}kynh@q&QfWWqUge@ zloGUbWEVlxi-#gK3&^pU9zy+EMOj!^qew}yK(L7^h6S=tTme)qc>7Z*lH+yF(&wEF zP+mjmwZL`z=2IdFJ)}c&d+(WW*V{0+oV1(mpz*Kf7-(|P2)MEl0A(a>Q!zQ}Z` zn{TB7ALhW-V@S?-R$N!DzY-#-z~tzm&eR!A60!FD^?6nNk065W#$sHoypzIcrAyze^;mIfBg+L@Xu)i8*Icwef7b|EoXssz^b_ zMaj>ba%<4lFyqj_qGGjzjA-na@ot4YXlCVi0W9P6;?4Lt-G2)$yaw7}*@USt&rx=A zS}Uimg7@3D$NdiNkZK^h~8i%k4{XwL8F^BCF(e6+^N=NakK1Co3W?cah{9CFkm z!j9l!N{zw%3@RFz4Z?VOLJU-v@?y^EQ1MnM*`F?eKQT$pRJ0!PMl@P}v^S1vC5uLw zn**or63Wr0PdCUhPK6RJ8J|+>1|cxnv5qD)s2NlWsNcVHtl@V^-KAfJ#fr5}ov@4r zNTA?c7grxadZ0vBQuTVe_@(i>i^R|s)Gvznxdd5E!k#4{;kcs)J$h%F@6r~S^KQuy zI;dtI+Sofst?WU%DZ?j&uokB_ZM9@HyC0kn;$gBwM6PLVKtC!(@Z85xSpD_2M4Yt~ zJOl(H%sy$SBamf&?zez0)6rqZA6;<)YJoW)#Q&(jtG+1HhS;yq8_Xml6)l<8rI{V$&UWITZJX`hY=nThe2~L@;Jk zc*1&0>n+JXBe6BIbk3t5;{1L*I0}oc{0ED=3I52rRXeAZ%X7G_rD;(D^LD3~*T~#q zlh?BPDp%XI2Imd6$9K8r2-d#We;e?iPgLA!Dgy6W1=pykKdL5r;L-Ng>hVkOX&-PIS+#b-62UqYX6eY5fZ2lp%H m5!to&NfES#l+ud8RUwaXl&2rj;y|Qb@;G19GNsKS&{FGbo+)Vn literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/361.p/254 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/361.p/254 new file mode 100644 index 0000000000000000000000000000000000000000..9070d3b954a5e663957f962510e06c345533bfd7 GIT binary patch literal 8128 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/362 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/362 new file mode 100644 index 0000000000000000000000000000000000000000..fb46c724b3ed577c220cc834c765632290e91d08 GIT binary patch literal 8192 zcmV+bAphSR{s`g20_4wkyscX%;#0KGuv3&XyaNeRh_TcWgJ7kHiIft-$Vr)%6?07} zY===yg-UAHt$?LApY`ET$$O_|?=K_#`346bU%6F8ALHEBU7fkWj4x08?X|7JLq^3d z;|6&m{Sv97IlU+9w5&U{ov}S9OvCUSj=W%hY6pj=aMbEGp$SWv8%0OoH0DR3`bw;v zqf{3JZMoqB7-i;tgN-F=Lf24t8ajFl&9*i#ti^RC*hF)xOV^hh5T^aX{{uix{V)~3 zZ!H#Q{1z0{+JIvp?%O}YCZg2sAft&jSNaPv<+`fYUE5ExsRdI>Oc1n(M*f4dY>l zdZ82RAxoBdiir~+j<$Ms>9^E?RgnS7Q(Z-W(u56|nB4reQaOtnSKVYqv0`X zQQm5FqwQ*>a~aO$?nv(9V< zYCu03H~A+1wGO#@Ev*vtN}B0U^9Ky_^QA(q_4Q|n}p*iR@?_s`6Lss}~=V^8J zJf7)z8J*-H%M{5?)8VOQ01nK@OmUtjZ#kTe?xS&*M7m$*6J3y z$Np0P?9~ommXS!beStjt_zsj_#n08^Rl`A~t~LZ9K9N?BZq5z~!ht{+cG$&aVlat6=epp{M17g= z^-Tv+iwq}!Q2ba4KUessp{4CWZ9qXNIgbgrdEQ@n9&|>;MpPV!5i^4Dif*=@EdTG^ zi>@j92(e1r6zSbTSK;ZQBG6)_IEVZYE~rrK)L=JmT

UNR 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/371 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/371 new file mode 100644 index 0000000000000000000000000000000000000000..66da9da417c8b546c486c3e730902d89389bb95d GIT binary patch literal 8192 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 O5JEArdD>1i;F#_)iv;Ze literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/374.p/180 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/0/x182/374.p/180 new file mode 100644 index 0000000000000000000000000000000000000000..0d252a09553ff20733b231d0c9c8f88af62dc232 GIT binary patch literal 5760 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/235 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/235 new file mode 100644 index 0000000000000000000000000000000000000000..a24031db8ad22d642d422b4f58af828ee86cf933 GIT binary patch literal 8192 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 literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/89 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/89 new file mode 100644 index 0000000000000000000000000000000000000000..6d2372f9c90ac13df6bd13918eec49de0cf9ae04 GIT binary patch literal 2848 zcmV+*3*Yos3)iEhpNaZR44S7U)Eo*s@adf^O%<;D^EXZC-iJyODG}{4HssA(M9{Sm zaC0Q2`tKNSswL*3t&1AvWyXo{IN#M1$n|aOF1pB|`wC+fK?8`IQX@m^oJ{T;*dV^p zgSS^Uiv3%-5#hLdP%?Idwh&$St#|uJky3VO(Z9XpN8Nc+o60CY3=E}QfnlV|kTbc2 zGI<`a00mXPf9bK(s@oN>pc<{6U5CskaxCY3*e3;eR61GD<4a|v#$*|aG(k>cYwJt7Iq`sp8RX?@;T2r!>;U+lWSJaHFEX|YEc{q z=7&zgYQrIJ!`kq&YDb0ylT3ZzLE*X}YdPTk$J4z`BfJ=LA!6DuFiFTJYKHQ;9dX79 zk2WL|Q>ls6EG=Wa&CZt+<9M5Q*pf^YM4B{B>~Igvb~gv>?kG& zF~5Ue^sUPMoaBeK$*cOk;C@dHV@-*<9(bXh5N}b>dTh0&sDXbr7AG$*6x$SPorX1f zIX{6PUr~jCHw-A9Z|>M*N7D^Cfrd)0Z~D~WD5r5Y_uN|+uPZDTmwHL;cU^rgqDb+v zj`PswFn&{!IX)F9sC8WoVdkj>gk=FPndxo#)9YH7CoB^-)n{X;p`A!ZR^ul z1YcSB$pJZXUp_G<)UMtL^@gXRH?nfeRqBiu!e-vnCMbzY3Q>N^Zbp!5n`}6B_?omo zbgV^hS}_z4jROF-d59d2`+t#i&qZV{qMr?6{ z-||@w31-WwUZHC-Ueh;Vcs7Ew>BFKxm^MB&2VhKZKM)cf$kBR$odLW~CRxmGZ5lZF*$Z1P)N-=u28QzY;t4n{$x$ z5!q<;olAlLFF0cB+rdk++&sI)I@FWag*7zg$#5#PO_X`5V6K*1mBm}DK%BeVo|9cp z@0JmVP#OLPU8mO^g1`_g}~ZN-Je!<$)%USA@c}J$ksveFfp_HIN*f zk&CmIqZkCrf#x+m+9-p@F8NJD#X|4vps@_ah1VeD}ixUUc>#Btw#IZ&$}5?zJm#mdld) z>fUUc0pWw)Fa;DKeJ96J?ex-y^_a!BAnDCCSX(x82LY-wb)V~IL+*xhk(=M`;h+3> z3T@!XyEd@xrKv>cG}47VgEsNuK$2rxoat5Q?#LHMcp{`DLhq|$B*`sV3^G$WTm`Mf z6LaN3)+GkkXV>BygKGAfZLDL?#KB?1(487>MnqXqTK{5R5dQM}`mhy_fpCto(^V6c zq}qEz9)!#9%w+`8+(SbWt`0^?q=vTXxVTwMdBYnQPn7p z`5dv)QF`@V`A0oqISnA8v%P*U=OBT&CEUc$z$liRREd^9(_Pg(WpK}JTJ|3y#o3q+ zMmd7P$~3q}1xSvNSdP+c=v|S1Seej-B7q45>azawM6p(GQ6Pw8p8XN6^LJoMl=4#4 z=#vhXw^z?UpLEyKc|?u}#i)STN@S7I^Rd*foU6dtMbc*0S zW3T7`F9sN@D?l7Wi`#keMMGbp9bjfjYluKw($&Z@>@e?Y2=D4v|KNTW!TJABzWuX> zh06B}08A=pydb}jHnOXnBmJjz^0P4QrrPRw&^bw}sBJlO3{H|;ZH@RX9)xWkhq)8;O;OZ&zITok8P=GHFY8%00U yJFG{Gw#KBh?%9=%lpIV+s&w!3B^-#Hx#6J#JJd&a-|CvQ_?j*Y0Y9fc)InK@r-`lr literal 0 HcmV?d00001 diff --git a/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/99 b/root/pkg/mod/cache/download/sumdb/sum.golang.org/tile/8/1/712.p/99 new file mode 100644 index 0000000000000000000000000000000000000000..9c84719425de299e12584f26cbd53557edc934ea GIT binary patch literal 3168 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 + github.event_name == 'push' || + github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - uses: actions/setup-go@v2 + with: + go-version: 1.18 + + - run: go test -json ./... > report.json + + - run: go test -coverprofile=coverage.out -json ./... > sonar-report.json + + - uses: sonarsource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/workflows/tests.yaml b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/workflows/tests.yaml new file mode 100644 index 0000000..658d363 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.github/workflows/tests.yaml @@ -0,0 +1,29 @@ +name: Unit Tests +on: [push, pull_request_target] + +jobs: + tests: + name: Tests (Go ${{ matrix.go-version }}) + runs-on: ubuntu-latest + # always run on push events + # only run on pull_request_target event when pull request pulls from fork repository + if: > + github.event_name == 'push' || + github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository + strategy: + fail-fast: false + matrix: + go-version: [1.19, "^1"] + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + + - run: ./sh/gofmt.sh + + - run: go vet ./... + + - run: go test -v -race ./... diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.gitignore b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.gitignore new file mode 100644 index 0000000..a1fb98b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.gitignore @@ -0,0 +1,23 @@ +.vscode + +# Test binary, build with `go test -c` +*.test + +# Key generated in test +cryptoutil/tmpKey.pem + +# Debug files +debug + +# Report files +sonar-report.json +coverage.out +report.json + +# idea files +.idea + +# DS_Store files +.DS_Store + + diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.golangci.yaml b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.golangci.yaml new file mode 100644 index 0000000..6799a1f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.golangci.yaml @@ -0,0 +1,26 @@ +# .golangci.yaml +run: + timeout: 5m # Example: set a timeout + issues: + # Disable SA1019 directly or per linter if needed + # https://golangci-lint.run/usage/configuration/#issues-configuration + exclude-rules: + # Exclude all SA1019 issues globally + - text: "SA1019" # Match the error message part for SA1019 (e.g., "SA1019: example is deprecated") + linters: + - staticcheck # Or specific linter if SA1019 comes from multiple sources + + # Alternatively, if SA1019 is specific to certain paths: + # - text: "SA1019" + # path: "internal/deprecated_stuff/" # Exclude issues in specific directories + # linters: + # - staticcheck + +linters: + # Explicitly enable linters you want to use. + # If empty, default linters are used. + enable: + - staticcheck # Ensure staticcheck is enabled if you're trying to ignore its rules + # - govet + # - errcheck + # ... other linters you need diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.pre-commit-config.yaml b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.pre-commit-config.yaml new file mode 100644 index 0000000..35f7ab9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +default_stages: + - commit +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.4.0 + hooks: + - id: trailing-whitespace + - id: check-yaml + + - repo: local + hooks: + - id: local-go-fmt + name: 'go fmt' + entry: sh/gofmt.sh + language: 'script' + description: "Go Format" + + - id: local-go-build-modtidy + name: 'go build & go mod tidy' + entry: sh/go-build-modtidy.sh + language: 'script' + description: "Go build & go mod tidy" + + - id: local-go-imports + name: 'tidy imports' + entry: sh/goimports.sh + language: 'script' + description: "Go imports" + + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.3.5 + hooks: + - id: go-vet + exclude: _examples + - id: go-cyclo + exclude: _examples + args: [ "-over", "15", "-ignore", ".pb.go" ] + - id: go-unit-tests + args: ["-race", "./..."] + stages: [ push ] # only run tests on push, not on commit diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/CONTRIBUTING.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/CONTRIBUTING.md new file mode 100644 index 0000000..543aa55 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing + +## Commit Process + +This repo comes with pre-commit hooks. These should be installed before commiting, done with: +```bash +pre-commit install --hook-type pre-commit --hook-type pre-push +``` +This will lint and run Go tools on commit, and run unit tests when pushing. diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/LICENSE.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/LICENSE.md new file mode 100644 index 0000000..fefdd34 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/LICENSE.md @@ -0,0 +1,23 @@ +# MIT License + +Copyright © 2017 Yoti Ltd + +* * * + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/README.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/README.md new file mode 100644 index 0000000..7a70c18 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/README.md @@ -0,0 +1,70 @@ +# Yoti Go SDK + +[![Build Status](https://github.com/getyoti/yoti-go-sdk/workflows/Unit%20Tests/badge.svg?branch=master)](https://github.com/getyoti/yoti-go-sdk/actions) +[![Go Report Card](https://goreportcard.com/badge/github.com/getyoti/yoti-go-sdk)](https://goreportcard.com/report/github.com/getyoti/yoti-go-sdk) +[![PkgGoDev](https://pkg.go.dev/badge/github.com/getyoti/yoti-go-sdk/v3)](https://pkg.go.dev/github.com/getyoti/yoti-go-sdk/v3?tab=doc) +[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://github.com/getyoti/yoti-go-sdk/blob/master/LICENSE.md) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=getyoti%3Ago&metric=coverage)](https://sonarcloud.io/dashboard?id=getyoti%3Ago) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=getyoti%3Ago&metric=bugs)](https://sonarcloud.io/dashboard?id=getyoti%3Ago) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=getyoti%3Ago&metric=code_smells)](https://sonarcloud.io/dashboard?id=getyoti%3Ago) +[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=getyoti%3Ago&metric=vulnerabilities)](https://sonarcloud.io/dashboard?id=getyoti%3Ago) + +Welcome to the Yoti Go SDK. This repo contains the tools and step by step instructions you need to quickly integrate your Go back-end with Yoti so that your users can share their identity details with your application in a secure and trusted way. + +## Table of Contents + +1) [Requirements](#requirements) - +Requirements to use the SDK + +1) [Enabling the SDK](#enabling-the-sdk) - +How to add the SDK to your project + +1) [Setup](#setup) - +Setup required before using the Yoti services + +1) [Products](#products) - +Links to more information about the products offered by the Yoti SDK + +1) [Support](#support) - +Please feel free to reach out + +## Requirements + +Supported Go Versions: +- See [./.github/workflows/test.yaml](./.github/workflows/test.yaml) for supported versions + +## Enabling the SDK + +Simply add `github.com/getyoti/yoti-go-sdk/v3` as an import: +```Go +import "github.com/getyoti/yoti-go-sdk/v3" +``` +or add the following line to your go.mod file (check https://github.com/getyoti/yoti-go-sdk/releases for the latest version) +``` + +require github.com/getyoti/yoti-go-sdk/v3 v3.14.0 +``` + +## Setup + +For each service you will need: + +* Your Client SDK ID, generated by [Yoti Hub](https://hub.yoti.com) when you create (and then publish) your app. +* Your .pem file. This is your own unique private key which your browser generates from the [Yoti Hub](https://hub.yoti.com) when you create an application. + +## Products + +- [Yoti app integration](_docs/PROFILE.md) - Connect with already-verified customers. + - [Yoti app sandbox](_docs/PROFILE_SANDBOX.md) - Use the Sandbox SDK in conjunction with the Yoti app integration. + +## Support + +For any questions or support please contact us here: https://support.yoti.com +Please provide the following to get you up and working as quickly as possible: + +* Computer type +* OS version +* Version of Go being used +* Screenshot + +Once we have answered your question we may contact you again to discuss Yoti products and services. If you’d prefer us not to do this, please let us know when you e-mail. diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV.md new file mode 100644 index 0000000..ba3b66c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV.md @@ -0,0 +1,9 @@ + # Yoti IDV (Doc Scan) + + ## About + Yoti IDV can be seamlessly integrated with your website, app or custom product so you can perform secure identity checks. You'll be able to request specific ID documents from users directly from your website or app. + + See the [Developer Docs](https://developers.yoti.com/identity-verification/getting-started) for more information. + + ## Running the example +- See the [_examples/idv](../_examples/idv) folder for instructions on how to run the IDV Example project \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV_SANDBOX.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV_SANDBOX.md new file mode 100644 index 0000000..5de276f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/IDV_SANDBOX.md @@ -0,0 +1,25 @@ +# Yoti Go IDV (Doc Scan) Sandbox Module + +This module contains the tools you need to test your Go back-end integration with the IDV Sandbox service. + +## Importing the Sandbox + +You can reference the sandbox by adding the following import: + +```Go +import "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox" +``` + +## Configuration +The sandbox is initialised in the following way: +```Go +sandboxClient := sandbox.NewClient(sandboxClientSdkId, privateKey) +``` +* `sandboxClientSdkId` is the Sandbox SDK identifier generated from the Sandbox section on Yoti Hub. +* `privateKey` is the PEM file for your Sandbox application downloaded from the Yoti Hub, in the Sandbox section. + +Please do not open the PEM file, as this might corrupt the key, and you will need to redownload it. + +## Examples + +- [IDV Sandbox WebDriver Example](../_examples/docscansandbox/) \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE.md new file mode 100644 index 0000000..76e418f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE.md @@ -0,0 +1,153 @@ +Go Yoti App Integration +============================= + +1) [An Architectural View](#an-architectural-view) - +High level overview of integration + +1) [Profile Retrieval](#profile-retrieval) - +How to retrieve a Yoti profile using the one time use token + +1) [Running the example](#running-the-example) - +Running the profile example + +1) [API Coverage](#api-coverage) - +Attributes defined + +## An Architectural View + +To integrate your application with Yoti, your back-end must expose a GET endpoint that Yoti will use to forward tokens. +The endpoint can be configured in Yoti Hub when you create/update your application. + +The image below shows how your application back-end and Yoti integrate in the context of a Login flow. +Yoti SDK carries out for you steps 6, 7, 8, and the profile decryption in step 9. + +![alt text](login_flow.png "Login flow") + +## Profile Retrieval + +When your application receives a one time use token via the exposed endpoint (it will be assigned to a query string parameter named `token`), you can easily retrieve the activity details by adding the following to your endpoint handler: + +```Go +var activityDetails profile.ActivityDetails +activityDetails, err = client.GetActivityDetails(yotiOneTimeUseToken) +if err != nil { + // handle unhappy path +} +``` + +### Handling Errors +If a network error occurs that can be handled by resending the request, +the error returned by the SDK will implement the temporary error interface. +This can be tested for using a type assertion, and resent. + +```Go +var activityDetails profile.ActivityDetails +activityDetails, err = client.GetActivityDetails(yotiOneTimeUseToken) +tempError, temporary := err.(interface { + Temporary() bool +}) +if !temporary || !tempError.Temporary() { + // can retry same request +} +``` + +### Retrieving the user profile + +You can then get the user profile from the activityDetails struct: + +```Go +var rememberMeID string = activityDetails.RememberMeID() +var parentRememberMeID string = activityDetails.ParentRememberMeID() +var userProfile profile.UserProfile = 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 +} else { + dateOfBirth = dobAttr.Value() +} +var structuredPostalAddress map[string]interface{} +structuredPostalAddressAttribute, err := userProfile.StructuredPostalAddress() +if err != nil { + // handle error +} else { + structuredPostalAddress := structuredPostalAddressAttribute.Value().(map[string]interface{}) +} +``` + +If you have chosen "Verify Condition" on the Yoti Hub 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) +``` + +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 you 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() +``` + +You can also retrieve further properties from these respective anchors in the following way: + +```Go +var givenNamesFirstAnchor *anchor.Anchor = givenNamesAnchors[0] + +var anchorType anchor.Type = givenNamesFirstAnchor.Type() +var signedTimestamp *time.Time = givenNamesFirstAnchor.SignedTimestamp().Timestamp() +var subType string = givenNamesFirstAnchor.SubType() +var value string = givenNamesFirstAnchor.Value() +``` + +## Running the Example + +Follow the below link for instructions on how to run the example project: + +1) [Profile example](../_examples/profile/README.md) + +## API Coverage + +* [X] Activity Details + * [X] Remember Me ID `RememberMeID()` + * [X] Parent Remember Me ID `ParentRememberMeID()` + * [X] User Profile `UserProfile` + * [X] Selfie `Selfie()` + * [X] Selfie Base64 URL `Selfie().Value().Base64URL()` + * [X] Given Names `GivenNames()` + * [X] Family Name `FamilyName()` + * [X] Full Name `FullName()` + * [X] Mobile Number `MobileNumber()` + * [X] Email Address `EmailAddress()` + * [X] Date of Birth `DateOfBirth()` + * [X] Postal Address `Address()` + * [X] Structured Postal Address `StructuredPostalAddress()` + * [X] Gender `Gender()` + * [X] Nationality `Nationality()` \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE_SANDBOX.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE_SANDBOX.md new file mode 100644 index 0000000..ffe7777 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/PROFILE_SANDBOX.md @@ -0,0 +1,30 @@ +# Yoti Go Profile Sandbox Module + +This module contains the tools you need to test your Go back-end integration with the Yoti Sandbox service. + +## Importing the Sandbox + +You can reference the sandbox by adding the following import: + +```Go +import "github.com/getyoti/yoti-go-sdk/v3/profile/sandbox" +``` + +## Configuration +The sandbox is initialised in the following way: +```Go +sandboxClient := sandbox.Client{ + ClientSdkID: sandboxClientSdkId, + Key: privateKey, + } +``` +* `sandboxClientSdkId` is the Sandbox SDK identifier generated from the Sandbox section on Yoti Hub. +* `privateKey` is the PEM file for your Sandbox application downloaded from the Yoti Hub, in the Sandbox section. + +Please do not open the PEM file, as this might corrupt the key, and you will need to redownload it. + +The format of `privateKey` passed in to the client needs to be `*rsa.PrivateKey`. See the [sandboxexample_test.go](../_examples/profilesandbox/sandboxexample_test.go) to see how to easily create this struct. + +## Examples + +- [Profile Sandbox Example](../_examples/profilesandbox/) \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/login_flow.png b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/_docs/login_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..ca0e4754dbb6c6b9999b6ce74912319faa9534c5 GIT binary patch literal 104616 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 len(ciphertext) { + err = errors.New("ciphertext is not padded with PKCS#7 padding") + return + } + + // verify all padding bytes are correct + for i := 0; i < n; i++ { + if ciphertext[len(ciphertext)-n+i] != c { + err = errors.New("ciphertext is not padded with PKCS#7 padding") + return + } + } + return ciphertext[:len(ciphertext)-n], nil +} + +// DecryptToken decrypts an RSA-encrypted token, using the specified RSA private key +func DecryptToken(encryptedConnectToken string, key *rsa.PrivateKey) (result string, err error) { + // token was encoded as a urlsafe base64 so it can be transferred in a url + var cipherBytes []byte + if cipherBytes, err = util.UrlSafeBase64ToBytes(encryptedConnectToken); err != nil { + return "", err + } + + var decipheredBytes []byte + if decipheredBytes, err = decryptRsa(cipherBytes, key); err != nil { + return "", err + } + + return string(decipheredBytes), nil +} + +// UnwrapKey unwraps an RSA private key +func UnwrapKey(wrappedKey string, key *rsa.PrivateKey) (result []byte, err error) { + var cipherBytes []byte + if cipherBytes, err = util.Base64ToBytes(wrappedKey); err != nil { + return nil, err + } + return decryptRsa(cipherBytes, key) +} + +func decryptAESGCM(cipherText, iv, secret []byte) ([]byte, error) { + block, err := aes.NewCipher(secret) + if err != nil { + return nil, fmt.Errorf("failed to create new aes cipher: %v", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("failed to create new gcm cipher: %v", err) + } + + plainText, err := gcm.Open(nil, iv, cipherText, nil) + if err != nil { + return nil, fmt.Errorf("failed to decrypt receipt key: %v", err) + } + + return plainText, nil +} + +func UnwrapReceiptKey(wrappedReceiptKey []byte, encryptedItemKey []byte, itemKeyIv []byte, key *rsa.PrivateKey) ([]byte, error) { + decryptedItemKey, err := decryptRsa(encryptedItemKey, key) + if err != nil { + return nil, fmt.Errorf("failed to decrypt item key: %v", err) + } + + plainText, err := decryptAESGCM(wrappedReceiptKey, itemKeyIv, decryptedItemKey) + if err != nil { + return nil, fmt.Errorf("failed to decrypt receipt key: %v", err) + } + return plainText, nil +} + +func DecryptReceiptContent(content, receiptContentKey []byte) ([]byte, error) { + if content == nil { + return nil, fmt.Errorf("failed to decrypt receipt content is nil") + } + + decodedData := &yotiprotocom.EncryptedData{} + err := proto.Unmarshal(content, decodedData) + if err != nil { + return nil, fmt.Errorf("failed to unmarshall content: %v", content) + } + + return DecipherAes(receiptContentKey, decodedData.Iv, decodedData.CipherText) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/cryptoutil/crypto_utils_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/cryptoutil/crypto_utils_test.go new file mode 100644 index 0000000..864e2f3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/cryptoutil/crypto_utils_test.go @@ -0,0 +1,176 @@ +package cryptoutil + +import ( + "crypto/aes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "os" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/file" + "github.com/getyoti/yoti-go-sdk/v3/util" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" +) + +const ( + wrappedKey = "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=" + b64EncryptedData = "ChCZAib1TBm9Q5GYfFrS1ep9EnAwQB5shpAPWLBgZgFgt6bCG3S5qmZHhrqUbQr3yL6yeLIDwbM7x4nuT/MYp+LDXgmFTLQNYbDTzrEzqNuO2ZPn9Kpg+xpbm9XtP7ZLw3Ep2BCmSqtnll/OdxAqLb4DTN4/wWdrjnFC+L/oQEECu646" + encryptedToken = "b6H19bUCJhwh6WqQX_sEHWX9RP-A_ANr1fkApwA4Dp2nJQFAjrF9e6YCXhNBpAIhfHnN0iXubyXxXZMNwNMSQ5VOxkqiytrvPykfKQWHC6ypSbfy0ex8ihndaAXG5FUF-qcU8QaFPMy6iF3x0cxnY0Ij0kZj0Ng2t6oiNafb7AhT-VGXxbFbtZu1QF744PpWMuH0LVyBsAa5N5GJw2AyBrnOh67fWMFDKTJRziP5qCW2k4h5vJfiYr_EOiWKCB1d_zINmUm94ZffGXxcDAkq-KxhN1ZuNhGlJ2fKcFh7KxV0BqlUWPsIEiwS0r9CJ2o1VLbEs2U_hCEXaqseEV7L29EnNIinEPVbL4WR7vkF6zQCbK_cehlk2Qwda-VIATqupRO5grKZN78R9lBitvgilDaoE7JB_VFcPoljGQ48kX0wje1mviX4oJHhuO8GdFITS5LTbojGVQWT7LUNgAUe0W0j-FLHYYck3v84OhWTqads5_jmnnLkp9bdJSRuJF0e8pNdePnn2lgF-GIcyW_0kyGVqeXZrIoxnObLpF-YeUteRBKTkSGFcy7a_V_DLiJMPmH8UXDLOyv8TVt3ppzqpyUrLN2JVMbL5wZ4oriL2INEQKvw_boDJjZDGeRlu5m1y7vGDNBRDo64-uQM9fRUULPw-YkABNwC0DeShswzT00=" +) + +func TestCryptoUtil_ParseRSAKey(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + block := pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + } + keyFileName := "tmpKey.pem" + keyFile, err := os.Create(keyFileName) + assert.NilError(t, err) + + err = pem.Encode(keyFile, &block) + assert.NilError(t, err) + + err = keyFile.Close() + assert.NilError(t, err) + + var keyBytes []byte + keyBytes, err = file.ReadFile(keyFileName) + assert.NilError(t, err) + + key, err = ParseRSAKey(keyBytes) + assert.NilError(t, err) + assert.Check(t, key != nil) +} + +func TestCryptoutil_ParseRSAKey_PublicKeyShouldFail(t *testing.T) { + testPEM := []byte(`-----BEGIN RSA PUBLIC KEY----- +VGVzdCBTdHJpbmc= +-----END RSA PUBLIC KEY-----`) + + _, err := ParseRSAKey(testPEM) + + assert.Error(t, err, "invalid key: not RSA private key") +} + +func TestCryptoutil_ParseRSAKey_InvalidKeyShouldFail(t *testing.T) { + testPEM := []byte(`-----BEGIN RSA PRIVATE KEY----- +VGVzdCBTdHJpbmc= +-----END RSA PRIVATE KEY-----`) + + _, err := ParseRSAKey(testPEM) + + assert.Error(t, err, "invalid key: bad RSA private key") +} + +func TestCryptoutil_ParseRSAKey_InvalidShouldFail(t *testing.T) { + var testPEM []byte + + _, err := ParseRSAKey(testPEM) + + assert.Error(t, err, "invalid key: not PEM-encoded") +} + +func TestCryptoutil_DecipherAes(t *testing.T) { + unwrappedKey, err := UnwrapKey(wrappedKey, getKey()) + if err != nil { + return + } + + encryptedBytes, err := util.Base64ToBytes(b64EncryptedData) + if err != nil || len(encryptedBytes) == 0 { + assert.NilError(t, err) + } + encryptedData := &yotiprotocom.EncryptedData{} + err = proto.Unmarshal(encryptedBytes, encryptedData) + assert.NilError(t, err) + + bytes, err := DecipherAes(unwrappedKey, encryptedData.Iv, encryptedData.CipherText) + assert.NilError(t, err) + assert.Check(t, bytes != nil) +} + +func TestCryptoutil_DecipherAes_EmptyCiphertextShouldError(t *testing.T) { + _, err := DecipherAes([]byte{}, []byte{}, []byte{}) + assert.Check(t, err != nil) +} + +func TestCryptoutil_DecipherAes_CiphertextNotMatchingBlocksizeShouldError(t *testing.T) { + _, err := DecipherAes( + make([]byte, 16), + []byte{}, + make([]byte, aes.BlockSize+1), + ) + assert.Check(t, err != nil) +} + +func TestCryptoutil_pkcs7Unpad_InvalidBlocksizeShouldError(t *testing.T) { + _, err := pkcs7Unpad([]byte{}, -1) + assert.Error(t, err, "blocksize -1 is not valid for padding removal") +} + +func TestCryptoutil_pkcs7Unpad_EmptyByteArrayShouldError(t *testing.T) { + _, err := pkcs7Unpad([]byte{}, 1) + assert.Error(t, err, "cannot remove padding on empty byte array") +} + +func TestCryptoutil_pkcs7Unpad_CiphertextNotMultipleOfBlocksizeShouldError(t *testing.T) { + _, err := pkcs7Unpad([]byte{0, 0, 0}, 2) + assert.Error(t, err, "ciphertext is not a multiple of the block size") +} + +func TestCryptoutil_pkcs7Unpad_CiphertextNotPaddedShouldError(t *testing.T) { + _, err := pkcs7Unpad([]byte{0xF, 0x0, 0x0}, 3) + assert.Error(t, err, "ciphertext is not padded with PKCS#7 padding") +} + +func TestCryptoutil_pkcs7Unpad_CiphertextPaddingIncorrect(t *testing.T) { + _, err := pkcs7Unpad([]byte{0x1, 0x1, 0x1, 0xF, 0x0, 0x0, 0xB, 0xA, 0x7}, 3) + assert.Error(t, err, "ciphertext is not padded with PKCS#7 padding") +} + +func TestCryptoutil_DecryptToken(t *testing.T) { + _, err := DecryptToken(encryptedToken, getKey()) + assert.NilError(t, err) +} + +func TestCryptoutil_DecryptToken_InvalidToken(t *testing.T) { + _, err := DecryptToken("c29tZS10b2tlbg==", getKey()) + assert.Error(t, err, "crypto/rsa: decryption error") +} + +func TestCryptoutil_DecryptToken_InvalidBase64ShouldError(t *testing.T) { + _, err := DecryptToken("Not a Token", nil) + assert.Check(t, err != nil) +} + +func TestCryptoutil_UnwrapKey(t *testing.T) { + unwrappedKey, err := UnwrapKey(wrappedKey, getKey()) + + assert.NilError(t, err) + assert.Check(t, unwrappedKey != nil) +} + +func TestCryptoutil_UnwrapKey_InvalidBase64ShouldError(t *testing.T) { + _, err := UnwrapKey("Not b64 encoded", nil) + assert.Check(t, err != nil) +} + +func getKey() (key *rsa.PrivateKey) { + keyBytes, err := os.ReadFile("../test/test-key.pem") + if err != nil { + panic("Error reading the test key: " + err.Error()) + } + + key, err = ParseRSAKey(keyBytes) + if err != nil { + panic("Error parsing the test key: " + err.Error()) + } + + return key +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client.go new file mode 100644 index 0000000..b074712 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client.go @@ -0,0 +1,88 @@ +package yoti + +import ( + "crypto/rsa" + "os" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/digitalidentity" + "github.com/getyoti/yoti-go-sdk/v3/requests" +) + +const DefaultURL = "https://api.yoti.com/share" + +// DigitalIdentityClient represents a client that can communicate with yoti and return information about Yoti users. +type DigitalIdentityClient struct { + // SdkID represents the SDK ID and NOT the App ID. This can be found in the integration section of your + // application hub at https://hub.yoti.com/ + SdkID string + + // Key should be the security key given to you by yoti (see: security keys section of + // https://hub.yoti.com) for more information about how to load your key from a file see: + // https://github.com/getyoti/yoti-go-sdk/blob/master/README.md + Key *rsa.PrivateKey + + apiURL string + HTTPClient requests.HttpClient // Mockable HTTP Client Interface +} + +// NewDigitalIdentityClient constructs a Client object +func NewDigitalIdentityClient(sdkID string, key []byte) (*DigitalIdentityClient, error) { + decodedKey, err := cryptoutil.ParseRSAKey(key) + + if err != nil { + return nil, err + } + + return &DigitalIdentityClient{ + SdkID: sdkID, + Key: decodedKey, + }, err +} + +// OverrideAPIURL overrides the default API URL for this Yoti Client +func (client *DigitalIdentityClient) OverrideAPIURL(apiURL string) { + client.apiURL = apiURL +} + +func (client *DigitalIdentityClient) getAPIURL() string { + if client.apiURL != "" { + return client.apiURL + } + + if value, exists := os.LookupEnv("YOTI_API_URL"); exists && value != "" { + return value + } + + return DefaultURL +} + +// GetSdkID gets the Client SDK ID attached to this client instance +func (client *DigitalIdentityClient) GetSdkID() string { + return client.SdkID +} + +// CreateShareSession creates a sharing session to initiate a sharing process based on a policy +func (client *DigitalIdentityClient) CreateShareSession(shareSessionRequest *digitalidentity.ShareSessionRequest) (shareSession *digitalidentity.ShareSession, err error) { + return digitalidentity.CreateShareSession(client.HTTPClient, shareSessionRequest, client.GetSdkID(), client.getAPIURL(), client.Key) +} + +// GetShareSession retrieves the sharing session. +func (client *DigitalIdentityClient) GetShareSession(sessionID string) (*digitalidentity.ShareSession, error) { + return digitalidentity.GetShareSession(client.HTTPClient, sessionID, client.GetSdkID(), client.getAPIURL(), client.Key) +} + +// CreateShareQrCode generates a sharing session QR code to initiate a sharing process based on session ID +func (client *DigitalIdentityClient) CreateShareQrCode(sessionID string) (share *digitalidentity.QrCode, err error) { + return digitalidentity.CreateShareQrCode(client.HTTPClient, sessionID, client.GetSdkID(), client.getAPIURL(), client.Key) +} + +// Get session QR code based on generated Qr ID +func (client *DigitalIdentityClient) GetQrCode(qrCodeId string) (share digitalidentity.ShareSessionQrCode, err error) { + return digitalidentity.GetShareSessionQrCode(client.HTTPClient, qrCodeId, client.GetSdkID(), client.getAPIURL(), client.Key) +} + +// GetShareReceipt fetches the receipt of the share given a receipt id. +func (client *DigitalIdentityClient) GetShareReceipt(receiptId string) (share digitalidentity.SharedReceiptResponse, err error) { + return digitalidentity.GetShareReceipt(client.HTTPClient, receiptId, client.GetSdkID(), client.getAPIURL(), client.Key) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client_test.go new file mode 100644 index 0000000..3c85d93 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digital_identity_client_test.go @@ -0,0 +1,168 @@ +package yoti + +import ( + "crypto/rsa" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/digitalidentity" + "github.com/getyoti/yoti-go-sdk/v3/test" + "gotest.tools/v3/assert" +) + +func TestDigitalIDClient(t *testing.T) { + key, err := os.ReadFile("./test/test-key.pem") + assert.NilError(t, err) + + _, err = NewDigitalIdentityClient("some-sdk-id", key) + assert.NilError(t, err) +} + +func TestDigitalIDClient_KeyLoad_Failure(t *testing.T) { + key, err := os.ReadFile("test/test-key-invalid-format.pem") + assert.NilError(t, err) + + _, err = NewDigitalIdentityClient("", key) + + assert.ErrorContains(t, err, "invalid key: not PEM-encoded") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestYotiClient_CreateShareSession(t *testing.T) { + key, err := os.ReadFile("./test/test-key.pem") + assert.NilError(t, err) + + client, err := NewDigitalIdentityClient("some-sdk-id", key) + assert.NilError(t, err) + + client.HTTPClient = &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"id":"SOME_ID","status":"SOME_STATUS","expiry":"SOME_EXPIRY","created":"SOME_CREATED","updated":"SOME_UPDATED","qrCode":{"id":"SOME_QRCODE_ID"},"receipt":{"id":"SOME_RECEIPT_ID"}}`)), + }, nil + }, + } + + policy, err := (&digitalidentity.PolicyBuilder{}).WithFullName().WithWantedRememberMe().Build() + assert.NilError(t, err) + + session, err := (&digitalidentity.ShareSessionRequestBuilder{}).WithPolicy(policy).Build() + assert.NilError(t, err) + + result, err := client.CreateShareSession(&session) + + assert.NilError(t, err) + assert.Equal(t, result.Status, "SOME_STATUS") +} + +func TestDigitalIDClient_HttpFailure_ReturnsUnKnownHttpError(t *testing.T) { + key := getDigitalValidKey() + client := DigitalIdentityClient{ + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 401, + }, nil + }, + }, + Key: key, + } + + _, err := client.GetShareSession("SOME ID") + + assert.ErrorContains(t, err, "unknown HTTP error") + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestDigitalIDClient_GetSession(t *testing.T) { + key, err := os.ReadFile("./test/test-key.pem") + if err != nil { + t.Fatalf("failed to read pem file :: %v", err) + } + + mockSessionID := "SOME_SESSION_ID" + client, err := NewDigitalIdentityClient("some-sdk-id", key) + if err != nil { + t.Fatalf("failed to build the DigitalIdClient :: %v", err) + } + + client.HTTPClient = &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"id":"SOME_ID","status":"SOME_STATUS","expiry":"SOME_EXPIRY","created":"SOME_CREATED","updated":"SOME_UPDATED","qrCode":{"id":"SOME_QRCODE_ID"},"receipt":{"id":"SOME_RECEIPT_ID"}}`)), + }, nil + }, + } + + result, err := client.GetShareSession(mockSessionID) + if err != nil { + t.Fatalf("failed to GetShareSesssion :: %v", err) + } + + assert.Equal(t, result.Id, "SOME_ID") + assert.Equal(t, result.Status, "SOME_STATUS") + assert.Equal(t, result.Created, "SOME_CREATED") + +} + +func TestDigitalIDClient_OverrideAPIURL_ShouldSetAPIURL(t *testing.T) { + client := &DigitalIdentityClient{} + + expectedURL := "expectedurl.com" + client.OverrideAPIURL(expectedURL) + + assert.Equal(t, client.getAPIURL(), expectedURL) +} + +func TestDigitalIDClient_GetAPIURLUsesOverriddenBaseUrlOverEnvVariable(t *testing.T) { + client := DigitalIdentityClient{} + client.OverrideAPIURL("overridenBaseUrl") + + os.Setenv("YOTI_API_URL", "envBaseUrl") + result := client.getAPIURL() + + assert.Equal(t, "overridenBaseUrl", result) +} + +func TestDigitalIDClient_GetAPIURLUsesEnvVariable(t *testing.T) { + client := DigitalIdentityClient{} + + os.Setenv("YOTI_API_URL", "envBaseUrl") + result := client.getAPIURL() + + assert.Equal(t, "envBaseUrl", result) +} + +func TestDigitalIDClient_GetAPIURLUsesDefaultUrlAsFallbackWithEmptyEnvValue(t *testing.T) { + client := DigitalIdentityClient{} + + os.Setenv("YOTI_API_URL", "") + result := client.getAPIURL() + + assert.Equal(t, "https://api.yoti.com/share", result) +} + +func TestDigitalIDClient_GetAPIURLUsesDefaultUrlAsFallbackWithNoEnvValue(t *testing.T) { + client := DigitalIdentityClient{} + + os.Unsetenv("YOTI_API_URL") + result := client.getAPIURL() + + assert.Equal(t, "https://api.yoti.com/share", result) +} + +func getDigitalValidKey() *rsa.PrivateKey { + return test.GetValidKey("test/test-key.pem") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/address.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/address.go new file mode 100644 index 0000000..17bfb51 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/address.go @@ -0,0 +1,52 @@ +package digitalidentity + +import ( + "reflect" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +func getFormattedAddress(profile *UserProfile, formattedAddress string) *yotiprotoattr.Attribute { + proto := getProtobufAttribute(*profile, consts.AttrStructuredPostalAddress) + + return &yotiprotoattr.Attribute{ + Name: consts.AttrAddress, + Value: []byte(formattedAddress), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: proto.Anchors, + } +} + +func ensureAddressProfile(p *UserProfile) *attribute.StringAttribute { + if structuredPostalAddress, err := p.StructuredPostalAddress(); err == nil { + if (structuredPostalAddress != nil && !reflect.DeepEqual(structuredPostalAddress, attribute.JSONAttribute{})) { + var formattedAddress string + formattedAddress, err = retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress.Value()) + if err == nil && formattedAddress != "" { + return attribute.NewString(getFormattedAddress(p, formattedAddress)) + } + } + } + + return nil +} + +func retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress interface{}) (address string, err error) { + parsedStructuredAddressMap := structuredPostalAddress.(map[string]interface{}) + if formattedAddress, ok := parsedStructuredAddressMap["formatted_address"]; ok { + return formattedAddress.(string), nil + } + return +} + +func getProtobufAttribute(profile UserProfile, key string) *yotiprotoattr.Attribute { + for _, v := range profile.attributeSlice { + if v.Name == key { + return v + } + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/application_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/application_profile.go new file mode 100644 index 0000000..8fae7bd --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/application_profile.go @@ -0,0 +1,50 @@ +package digitalidentity + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// Attribute names for application attributes +const ( + AttrConstApplicationName = "application_name" + AttrConstApplicationURL = "application_url" + AttrConstApplicationLogo = "application_logo" + AttrConstApplicationReceiptBGColor = "application_receipt_bgcolor" +) + +// ApplicationProfile is the profile of an application with convenience methods +// to access well-known attributes. +type ApplicationProfile struct { + baseProfile +} + +func newApplicationProfile(attributes *yotiprotoattr.AttributeList) ApplicationProfile { + return ApplicationProfile{ + baseProfile{ + attributeSlice: createAttributeSlice(attributes), + }, + } +} + +// ApplicationName is the name of the application +func (p ApplicationProfile) ApplicationName() *attribute.StringAttribute { + return p.GetStringAttribute(AttrConstApplicationName) +} + +// ApplicationURL is the URL where the application is available at +func (p ApplicationProfile) ApplicationURL() *attribute.StringAttribute { + return p.GetStringAttribute(AttrConstApplicationURL) +} + +// ApplicationReceiptBgColor is the background colour that will be displayed on +// each receipt the user gets as a result of a share with the application. +func (p ApplicationProfile) ApplicationReceiptBgColor() *attribute.StringAttribute { + return p.GetStringAttribute(AttrConstApplicationReceiptBGColor) +} + +// ApplicationLogo is the logo of the application that will be displayed to +// those users that perform a share with it. +func (p ApplicationProfile) ApplicationLogo() *attribute.ImageAttribute { + return p.GetImageAttribute(AttrConstApplicationLogo) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications.go new file mode 100644 index 0000000..a7655d0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications.go @@ -0,0 +1,34 @@ +package attribute + +import ( + "strconv" + "strings" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// AgeVerification encapsulates the result of a single age verification +// as part of a share +type AgeVerification struct { + Age int + CheckType string + Result bool + Attribute *yotiprotoattr.Attribute +} + +// NewAgeVerification constructs an AgeVerification from a protobuffer +func NewAgeVerification(attr *yotiprotoattr.Attribute) (verification AgeVerification, err error) { + split := strings.Split(attr.Name, ":") + verification.Age, err = strconv.Atoi(split[1]) + verification.CheckType = split[0] + + if string(attr.Value) == "true" { + verification.Result = true + } else { + verification.Result = false + } + + verification.Attribute = attr + + return +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications_test.go new file mode 100644 index 0000000..b3a6e08 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/age_verifications_test.go @@ -0,0 +1,42 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestNewAgeVerification_ValueTrue(t *testing.T) { + attribute := &yotiprotoattr.Attribute{ + Name: "age_over:18", + Value: []byte("true"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + ageVerification, err := NewAgeVerification(attribute) + + assert.NilError(t, err) + + assert.Equal(t, ageVerification.Age, 18) + assert.Equal(t, ageVerification.CheckType, "age_over") + assert.Equal(t, ageVerification.Result, true) +} + +func TestNewAgeVerification_ValueFalse(t *testing.T) { + attribute := &yotiprotoattr.Attribute{ + Name: "age_under:30", + Value: []byte("false"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + ageVerification, err := NewAgeVerification(attribute) + + assert.NilError(t, err) + + assert.Equal(t, ageVerification.Age, 30) + assert.Equal(t, ageVerification.CheckType, "age_under") + assert.Equal(t, ageVerification.Result, false) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser.go new file mode 100644 index 0000000..d1476c4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser.go @@ -0,0 +1,110 @@ +package anchor + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" + "google.golang.org/protobuf/proto" +) + +type anchorExtension struct { + Extension string `asn1:"tag:0,utf8"` +} + +var ( + sourceOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 47127, 1, 1, 1} + verifierOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 47127, 1, 1, 2} +) + +// ParseAnchors takes a slice of protobuf anchors, parses them, and returns a slice of Yoti SDK Anchors +func ParseAnchors(protoAnchors []*yotiprotoattr.Anchor) []*Anchor { + var processedAnchors []*Anchor + for _, protoAnchor := range protoAnchors { + parsedCerts := parseCertificates(protoAnchor.OriginServerCerts) + + anchorType, extension := getAnchorValuesFromCertificate(parsedCerts) + + parsedSignedTimestamp, err := parseSignedTimestamp(protoAnchor.SignedTimeStamp) + if err != nil { + continue + } + + processedAnchor := newAnchor(anchorType, parsedCerts, parsedSignedTimestamp, protoAnchor.SubType, extension) + + processedAnchors = append(processedAnchors, processedAnchor) + } + + return processedAnchors +} + +func getAnchorValuesFromCertificate(parsedCerts []*x509.Certificate) (anchorType Type, extension string) { + defaultAnchorType := TypeUnknown + + for _, cert := range parsedCerts { + for _, ext := range cert.Extensions { + var ( + value string + err error + ) + parsedAnchorType, value, err := parseExtension(ext) + if err != nil { + continue + } else if parsedAnchorType == TypeUnknown { + continue + } + return parsedAnchorType, value + } + } + + return defaultAnchorType, "" +} + +func parseExtension(ext pkix.Extension) (anchorType Type, val string, err error) { + anchorType = TypeUnknown + + switch { + case ext.Id.Equal(sourceOID): + anchorType = TypeSource + case ext.Id.Equal(verifierOID): + anchorType = TypeVerifier + default: + return anchorType, "", nil + } + + var ae anchorExtension + _, err = asn1.Unmarshal(ext.Value, &ae) + switch { + case err != nil: + return anchorType, "", fmt.Errorf("unable to unmarshal extension: %v", err) + case len(ae.Extension) == 0: + return anchorType, "", errors.New("empty extension") + default: + val = ae.Extension + } + + return anchorType, val, nil +} + +func parseSignedTimestamp(rawBytes []byte) (*yotiprotocom.SignedTimestamp, error) { + signedTimestamp := &yotiprotocom.SignedTimestamp{} + if err := proto.Unmarshal(rawBytes, signedTimestamp); err != nil { + return signedTimestamp, err + } + + return signedTimestamp, nil +} + +func parseCertificates(rawCerts [][]byte) (result []*x509.Certificate) { + for _, cert := range rawCerts { + parsedCertificate, _ := x509.ParseCertificate(cert) + + result = append(result, parsedCertificate) + } + + return result +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser_test.go new file mode 100644 index 0000000..13849a3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchor_parser_test.go @@ -0,0 +1,147 @@ +package anchor + +import ( + "crypto/x509/pkix" + "math/big" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" +) + +func assertServerCertSerialNo(t *testing.T, expectedSerialNo string, actualSerialNo *big.Int) { + expectedSerialNoBigInt := new(big.Int) + expectedSerialNoBigInt, ok := expectedSerialNoBigInt.SetString(expectedSerialNo, 10) + assert.Assert(t, ok, "Unexpected error when setting string as big int") + + assert.Equal(t, expectedSerialNoBigInt.Cmp(actualSerialNo), 0) // 0 == equivalent +} + +func createAnchorSliceFromTestFile(t *testing.T, filename string) []*yotiprotoattr.Anchor { + anchorBytes := test.DecodeTestFile(t, filename) + + protoAnchor := &yotiprotoattr.Anchor{} + err2 := proto.Unmarshal(anchorBytes, protoAnchor) + assert.NilError(t, err2) + + protoAnchors := append([]*yotiprotoattr.Anchor{}, protoAnchor) + + return protoAnchors +} + +func TestAnchorParser_parseExtension_ShouldErrorForInvalidExtension(t *testing.T) { + invalidExt := pkix.Extension{ + Id: sourceOID, + } + + _, _, err := parseExtension(invalidExt) + + assert.Check(t, err != nil) + assert.Error(t, err, "unable to unmarshal extension: asn1: syntax error: sequence truncated") +} + +func TestAnchorParser_Passport(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_passport.txt") + + parsedAnchors := ParseAnchors(anchorSlice) + + actualAnchor := parsedAnchors[0] + + assert.Equal(t, actualAnchor.Type(), TypeSource) + + expectedDate := time.Date(2018, time.April, 12, 13, 14, 32, 835537e3, time.UTC) + actualDate := actualAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "OCR" + assert.Equal(t, actualAnchor.SubType(), expectedSubType) + + expectedValue := "PASSPORT" + assert.Equal(t, actualAnchor.Value(), expectedValue) + + actualSerialNo := actualAnchor.OriginServerCerts()[0].SerialNumber + assertServerCertSerialNo(t, "277870515583559162487099305254898397834", actualSerialNo) +} + +func TestAnchorParser_DrivingLicense(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_driving_license.txt") + + parsedAnchors := ParseAnchors(anchorSlice) + resultAnchor := parsedAnchors[0] + + assert.Equal(t, resultAnchor.Type(), TypeSource) + + expectedDate := time.Date(2018, time.April, 11, 12, 13, 3, 923537e3, time.UTC) + actualDate := resultAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "" + assert.Equal(t, resultAnchor.SubType(), expectedSubType) + + expectedValue := "DRIVING_LICENCE" + assert.Equal(t, resultAnchor.Value(), expectedValue) + + actualSerialNo := resultAnchor.OriginServerCerts()[0].SerialNumber + assertServerCertSerialNo(t, "46131813624213904216516051554755262812", actualSerialNo) +} + +func TestAnchorParser_UnknownAnchor(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_unknown.txt") + + resultAnchor := ParseAnchors(anchorSlice)[0] + + expectedDate := time.Date(2019, time.March, 5, 10, 45, 11, 840037e3, time.UTC) + actualDate := resultAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "TEST UNKNOWN SUB TYPE" + expectedType := TypeUnknown + assert.Equal(t, resultAnchor.SubType(), expectedSubType) + assert.Equal(t, resultAnchor.Type(), expectedType) + assert.Equal(t, resultAnchor.Value(), "") +} + +func TestAnchorParser_YotiAdmin(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_yoti_admin.txt") + + resultAnchor := ParseAnchors(anchorSlice)[0] + + assert.Equal(t, resultAnchor.Type(), TypeVerifier) + + expectedDate := time.Date(2018, time.April, 11, 12, 13, 4, 95238e3, time.UTC) + actualDate := resultAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "" + assert.Equal(t, resultAnchor.SubType(), expectedSubType) + + expectedValue := "YOTI_ADMIN" + assert.Equal(t, resultAnchor.Value(), expectedValue) + + actualSerialNo := resultAnchor.OriginServerCerts()[0].SerialNumber + assertServerCertSerialNo(t, "256616937783084706710155170893983549581", actualSerialNo) +} + +func TestAnchors_None(t *testing.T) { + var anchorSlice []*Anchor + + sources := GetSources(anchorSlice) + assert.Equal(t, len(sources), 0, "GetSources should not return anything with empty anchors") + + verifiers := GetVerifiers(anchorSlice) + assert.Equal(t, len(verifiers), 0, "GetVerifiers should not return anything with empty anchors") +} + +func TestAnchorParser_InvalidSignedTimestamp(t *testing.T) { + var protoAnchors []*yotiprotoattr.Anchor + protoAnchors = append(protoAnchors, &yotiprotoattr.Anchor{ + SignedTimeStamp: []byte("invalidProto"), + }) + parsedAnchors := ParseAnchors(protoAnchors) + + var expectedAnchors []*Anchor + assert.DeepEqual(t, expectedAnchors, parsedAnchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors.go new file mode 100644 index 0000000..839a6e1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors.go @@ -0,0 +1,105 @@ +package anchor + +import ( + "crypto/x509" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" +) + +// Anchor is the metadata associated with an attribute. It describes how an attribute has been provided +// to Yoti (SOURCE Anchor) and how it has been verified (VERIFIER Anchor). +// If an attribute has only one SOURCE Anchor with the value set to +// "USER_PROVIDED" and zero VERIFIER Anchors, then the attribute +// is a self-certified one. +type Anchor struct { + anchorType Type + originServerCerts []*x509.Certificate + signedTimestamp SignedTimestamp + subtype string + value string +} + +func newAnchor(anchorType Type, originServerCerts []*x509.Certificate, signedTimestamp *yotiprotocom.SignedTimestamp, subtype string, value string) *Anchor { + return &Anchor{ + anchorType: anchorType, + originServerCerts: originServerCerts, + signedTimestamp: convertSignedTimestamp(signedTimestamp), + subtype: subtype, + value: value, + } +} + +// Type Anchor type, based on the Object Identifier (OID) +type Type int + +const ( + // TypeUnknown - default value + TypeUnknown Type = 1 + iota + // TypeSource - how the anchor has been sourced + TypeSource + // TypeVerifier - how the anchor has been verified + TypeVerifier +) + +// Type of the Anchor - most likely either SOURCE or VERIFIER, but it's +// possible that new Anchor types will be added in future. +func (a Anchor) Type() Type { + return a.anchorType +} + +// OriginServerCerts are the X.509 certificate chain(DER-encoded ASN.1) +// from the service that assigned the attribute. +// +// The first certificate in the chain holds the public key that can be +// used to verify the Signature field; any following entries (zero or +// more) are for intermediate certificate authorities (in order). +// +// The last certificate in the chain must be verified against the Yoti root +// CA certificate. An extension in the first certificate holds the main artifact type, +// e.g. “PASSPORT”, which can be retrieved with .Value(). +func (a Anchor) OriginServerCerts() []*x509.Certificate { + return a.originServerCerts +} + +// SignedTimestamp is the time at which the signature was created. The +// 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 *time.Time can be called with +// .Timestamp() on the result of this function. +func (a Anchor) SignedTimestamp() SignedTimestamp { + return a.signedTimestamp +} + +// SubType is an indicator of any specific processing method, or +// subcategory, pertaining to an artifact. For example, for a passport, this would be +// either "NFC" or "OCR". +func (a Anchor) SubType() string { + return a.subtype +} + +// Value identifies the provider that either sourced or verified the attribute value. +// The range of possible values is not limited. For a SOURCE anchor, expect a value like +// PASSPORT, DRIVING_LICENSE. For a VERIFIER anchor, expect a value like YOTI_ADMIN. +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, TypeSource) +} + +// 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, TypeVerifier) +} + +func filterAnchors(anchors []*Anchor, anchorType Type) (result []*Anchor) { + for _, v := range anchors { + if v.anchorType == anchorType { + result = append(result, v) + } + } + return result +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors_test.go new file mode 100644 index 0000000..ed5287e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/anchors_test.go @@ -0,0 +1,20 @@ +package anchor + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestFilterAnchors_FilterSources(t *testing.T) { + anchorSlice := []*Anchor{ + {subtype: "a", anchorType: TypeSource}, + {subtype: "b", anchorType: TypeVerifier}, + {subtype: "c", anchorType: TypeSource}, + } + sources := filterAnchors(anchorSlice, TypeSource) + assert.Equal(t, len(sources), 2) + assert.Equal(t, sources[0].subtype, "a") + assert.Equal(t, sources[1].subtype, "c") + +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/signed_timestamp.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/signed_timestamp.go new file mode 100644 index 0000000..2081b7d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/anchor/signed_timestamp.go @@ -0,0 +1,35 @@ +package anchor + +import ( + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" +) + +// SignedTimestamp is the object which contains a timestamp +type SignedTimestamp struct { + version int32 + timestamp *time.Time +} + +func convertSignedTimestamp(protoSignedTimestamp *yotiprotocom.SignedTimestamp) SignedTimestamp { + uintTimestamp := protoSignedTimestamp.Timestamp + intTimestamp := int64(uintTimestamp) + unixTime := time.Unix(intTimestamp/1e6, (intTimestamp%1e6)*1e3) + + return SignedTimestamp{ + version: protoSignedTimestamp.Version, + timestamp: &unixTime, + } +} + +// Version indicates both the version of the protobuf message in use, +// as well as the specific hash algorithms. +func (s SignedTimestamp) Version() int32 { + return s.version +} + +// Timestamp is a point in time, to the nearest microsecond. +func (s SignedTimestamp) Timestamp() *time.Time { + return s.timestamp +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_details.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_details.go new file mode 100644 index 0000000..a380150 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_details.go @@ -0,0 +1,48 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" +) + +// attributeDetails is embedded in each attribute for fields common to all +// attributes +type attributeDetails struct { + name string + contentType string + anchors []*anchor.Anchor + id *string +} + +// Name gets the attribute name +func (a attributeDetails) Name() string { + return a.name +} + +// ID gets the attribute ID +func (a attributeDetails) ID() *string { + return a.id +} + +// ContentType gets the attribute's content type description +func (a attributeDetails) ContentType() string { + return a.contentType +} + +// 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 attributeDetails) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value +// was acquired. +func (a attributeDetails) Sources() []*anchor.Anchor { + return anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value +// was verified by another provider. +func (a attributeDetails) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_test.go new file mode 100644 index 0000000..67b6c2b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/attribute_test.go @@ -0,0 +1,36 @@ +package attribute + +import ( + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func TestNewThirdPartyAttribute(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_third_party.txt") + + stringAttribute := NewString(protoAttribute) + + assert.Equal(t, stringAttribute.Value(), "test-third-party-attribute-0") + assert.Equal(t, stringAttribute.Name(), "com.thirdparty.id") + + assert.Equal(t, stringAttribute.Sources()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Sources()[0].SubType(), "orgName") + + assert.Equal(t, stringAttribute.Verifiers()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Verifiers()[0].SubType(), "orgName") +} + +func TestAttribute_DateOfBirth(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_date_of_birth.txt") + + dateOfBirthAttribute, err := NewDate(protoAttribute) + + assert.NilError(t, err) + + expectedDateOfBirth := time.Date(1970, time.December, 01, 0, 0, 0, 0, time.UTC) + actualDateOfBirth := dateOfBirthAttribute.Value() + + assert.Assert(t, actualDateOfBirth.Equal(expectedDateOfBirth)) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute.go new file mode 100644 index 0000000..cdc55ce --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute.go @@ -0,0 +1,39 @@ +package attribute + +import ( + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// DateAttribute is a Yoti attribute which returns a date as *time.Time for its value +type DateAttribute struct { + attributeDetails + value *time.Time +} + +// NewDate creates a new Date attribute +func NewDate(a *yotiprotoattr.Attribute) (*DateAttribute, error) { + parsedTime, err := time.Parse("2006-01-02", string(a.Value)) + if err != nil { + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &DateAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: &parsedTime, + }, nil +} + +// Value returns the value of the TimeAttribute as *time.Time +func (a *DateAttribute) Value() *time.Time { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute_test.go new file mode 100644 index 0000000..24807c9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/date_attribute_test.go @@ -0,0 +1,44 @@ +package attribute + +import ( + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestTimeAttribute_NewDate_DateOnly(t *testing.T) { + proto := yotiprotoattr.Attribute{ + Value: []byte("2011-12-25"), + } + + timeAttribute, err := NewDate(&proto) + assert.NilError(t, err) + + assert.Equal(t, *timeAttribute.Value(), time.Date(2011, 12, 25, 0, 0, 0, 0, time.UTC)) +} + +func TestTimeAttribute_DateOfBirth(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_date_of_birth.txt") + + dateOfBirthAttribute, err := NewDate(protoAttribute) + + assert.NilError(t, err) + + expectedDateOfBirth := time.Date(1970, time.December, 01, 0, 0, 0, 0, time.UTC) + actualDateOfBirth := dateOfBirthAttribute.Value() + + assert.Assert(t, actualDateOfBirth.Equal(expectedDateOfBirth)) +} + +func TestNewTime_ShouldReturnErrorForInvalidDate(t *testing.T) { + proto := yotiprotoattr.Attribute{ + Name: "example", + Value: []byte("2006-60-20"), + ContentType: yotiprotoattr.ContentType_DATE, + } + attribute, err := NewDate(&proto) + assert.Check(t, attribute == nil) + assert.ErrorContains(t, err, "month out of range") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition.go new file mode 100644 index 0000000..b0d4b8a --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition.go @@ -0,0 +1,31 @@ +package attribute + +import ( + "encoding/json" +) + +// Definition contains information about the attribute(s) issued by a third party. +type Definition struct { + name string +} + +// Name of the attribute to be issued. +func (a Definition) Name() string { + return a.name +} + +// MarshalJSON returns encoded json +func (a Definition) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Name string `json:"name"` + }{ + Name: a.name, + }) +} + +// NewAttributeDefinition returns a new AttributeDefinition +func NewAttributeDefinition(s string) Definition { + return Definition{ + name: s, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition_test.go new file mode 100644 index 0000000..b209e02 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/definition_test.go @@ -0,0 +1,18 @@ +package attribute + +import ( + "encoding/json" + "fmt" +) + +func ExampleDefinition_MarshalJSON() { + exampleDefinition := NewAttributeDefinition("exampleDefinition") + marshalledJSON, err := json.Marshal(exampleDefinition) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"exampleDefinition"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute.go new file mode 100644 index 0000000..a18ccab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute.go @@ -0,0 +1,87 @@ +package attribute + +import ( + "fmt" + "strings" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +const ( + documentDetailsDateFormatConst = "2006-01-02" +) + +// DocumentDetails represents information extracted from a document provided by the user +type DocumentDetails struct { + DocumentType string + IssuingCountry string + DocumentNumber string + ExpirationDate *time.Time + IssuingAuthority string +} + +// DocumentDetailsAttribute wraps a document details with anchor data +type DocumentDetailsAttribute struct { + attributeDetails + value DocumentDetails +} + +// Value returns the document details struct attached to this attribute +func (attr *DocumentDetailsAttribute) Value() DocumentDetails { + return attr.value +} + +// NewDocumentDetails creates a DocumentDetailsAttribute which wraps a +// DocumentDetails with anchor data +func NewDocumentDetails(a *yotiprotoattr.Attribute) (*DocumentDetailsAttribute, error) { + parsedAnchors := anchor.ParseAnchors(a.Anchors) + details := DocumentDetails{} + err := details.Parse(string(a.Value)) + if err != nil { + return nil, err + } + + return &DocumentDetailsAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: details, + }, nil +} + +// Parse fills a DocumentDetails object from a raw string +func (details *DocumentDetails) Parse(data string) error { + dataSlice := strings.Split(data, " ") + + if len(dataSlice) < 3 { + return fmt.Errorf("Document Details data is invalid, %s", data) + } + for _, section := range dataSlice { + if section == "" { + return fmt.Errorf("Document Details data is invalid %s", data) + } + } + + details.DocumentType = dataSlice[0] + details.IssuingCountry = dataSlice[1] + details.DocumentNumber = dataSlice[2] + if len(dataSlice) > 3 && dataSlice[3] != "-" { + expirationDateData, dateErr := time.Parse(documentDetailsDateFormatConst, dataSlice[3]) + + if dateErr == nil { + details.ExpirationDate = &expirationDateData + } else { + return dateErr + } + } + if len(dataSlice) > 4 { + details.IssuingAuthority = dataSlice[4] + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute_test.go new file mode 100644 index 0000000..bf2e7de --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/document_details_attribute_test.go @@ -0,0 +1,185 @@ +package attribute + +import ( + "fmt" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func ExampleDocumentDetails_Parse() { + raw := "PASSPORT GBR 1234567 2022-09-12" + details := DocumentDetails{} + err := details.Parse(raw) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Printf( + "Document Type: %s, Issuing Country: %s, Document Number: %s, Expiration Date: %s", + details.DocumentType, + details.IssuingCountry, + details.DocumentNumber, + details.ExpirationDate, + ) + // Output: Document Type: PASSPORT, Issuing Country: GBR, Document Number: 1234567, Expiration Date: 2022-09-12 00:00:00 +0000 UTC +} + +func ExampleNewDocumentDetails() { + proto := yotiprotoattr.Attribute{ + Name: "exampleDocumentDetails", + Value: []byte("PASSPORT GBR 1234567 2022-09-12"), + ContentType: yotiprotoattr.ContentType_STRING, + } + attribute, err := NewDocumentDetails(&proto) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Printf( + "Document Type: %s, With %d Anchors", + attribute.Value().DocumentType, + len(attribute.Anchors()), + ) + // Output: Document Type: PASSPORT, With 0 Anchors +} + +func TestDocumentDetailsShouldParseDrivingLicenceWithoutExpiry(t *testing.T) { + drivingLicenceGBR := "PASS_CARD GBR 1234abc - DVLA" + + details := DocumentDetails{} + err := details.Parse(drivingLicenceGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "PASS_CARD") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Assert(t, details.ExpirationDate == nil) + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "DVLA") +} + +func TestDocumentDetailsShouldParseRedactedAadhar(t *testing.T) { + aadhaar := "AADHAAR IND ****1234 2016-05-01" + details := DocumentDetails{} + err := details.Parse(aadhaar) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "AADHAAR") + assert.Equal(t, details.DocumentNumber, "****1234") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "IND") + assert.Equal(t, details.IssuingAuthority, "") +} + +func TestDocumentDetailsShouldParseSpecialCharacters(t *testing.T) { + testData := [][]string{ + {"type country **** - authority", "****"}, + {"type country ~!@#$%^&*()-_=+[]{}|;':,./<>? - authority", "~!@#$%^&*()-_=+[]{}|;':,./<>?"}, + {"type country \"\" - authority", "\"\""}, + {"type country \\ - authority", "\\"}, + {"type country \" - authority", "\""}, + {"type country '' - authority", "''"}, + {"type country ' - authority", "'"}, + } + for _, row := range testData { + details := DocumentDetails{} + err := details.Parse(row[0]) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentNumber, row[1]) + } +} + +func TestDocumentDetailsShouldFailOnDoubleSpace(t *testing.T) { + data := "AADHAAR IND ****1234" + details := DocumentDetails{} + err := details.Parse(data) + assert.Check(t, err != nil) + assert.ErrorContains(t, err, "Document Details data is invalid") +} + +func TestDocumentDetailsShouldParseDrivingLicenceWithExtraAttribute(t *testing.T) { + drivingLicenceGBR := "DRIVING_LICENCE GBR 1234abc 2016-05-01 DVLA someThirdAttribute" + details := DocumentDetails{} + err := details.Parse(drivingLicenceGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "DRIVING_LICENCE") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "DVLA") +} + +func TestDocumentDetailsShouldParseDrivingLicenceWithAllOptionalAttributes(t *testing.T) { + drivingLicenceGBR := "DRIVING_LICENCE GBR 1234abc 2016-05-01 DVLA" + + details := DocumentDetails{} + err := details.Parse(drivingLicenceGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "DRIVING_LICENCE") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "DVLA") +} + +func TestDocumentDetailsShouldParseAadhaar(t *testing.T) { + aadhaar := "AADHAAR IND 1234abc 2016-05-01" + + details := DocumentDetails{} + err := details.Parse(aadhaar) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "AADHAAR") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "IND") +} + +func TestDocumentDetailsShouldParsePassportWithMandatoryFieldsOnly(t *testing.T) { + passportGBR := "PASSPORT GBR 1234abc" + + details := DocumentDetails{} + err := details.Parse(passportGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "PASSPORT") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Assert(t, details.ExpirationDate == nil) + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "") +} + +func TestDocumentDetailsShouldErrorOnEmptyString(t *testing.T) { + empty := "" + + details := DocumentDetails{} + err := details.Parse(empty) + assert.ErrorContains(t, err, "Document Details data is invalid") +} + +func TestDocumentDetailsShouldErrorIfLessThan3Words(t *testing.T) { + corrupt := "PASS_CARD GBR" + details := DocumentDetails{} + err := details.Parse(corrupt) + assert.ErrorContains(t, err, "Document Details data is invalid") +} + +func TestDocumentDetailsShouldErrorForInvalidExpirationDate(t *testing.T) { + corrupt := "PASSPORT GBR 1234abc X016-05-01" + details := DocumentDetails{} + err := details.Parse(corrupt) + assert.ErrorContains(t, err, "cannot parse") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute.go new file mode 100644 index 0000000..c729e30 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute.go @@ -0,0 +1,38 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// GenericAttribute is a Yoti attribute which returns a generic value +type GenericAttribute struct { + attributeDetails + value interface{} +} + +// NewGeneric creates a new generic attribute +func NewGeneric(a *yotiprotoattr.Attribute) *GenericAttribute { + value, err := parseValue(a.ContentType, a.Value) + + if err != nil { + return nil + } + + var parsedAnchors = anchor.ParseAnchors(a.Anchors) + + return &GenericAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: value, + } +} + +// Value returns the value of the GenericAttribute as an interface +func (a *GenericAttribute) Value() interface{} { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute_test.go new file mode 100644 index 0000000..e2daae8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/generic_attribute_test.go @@ -0,0 +1,39 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestNewGeneric_ShouldParseUnknownTypeAsString(t *testing.T) { + value := []byte("value") + protoAttr := yotiprotoattr.Attribute{ + ContentType: yotiprotoattr.ContentType_UNDEFINED, + Value: value, + } + parsed := NewGeneric(&protoAttr) + + stringValue, ok := parsed.Value().(string) + assert.Check(t, ok) + + assert.Equal(t, stringValue, string(value)) +} + +func TestGeneric_ContentType(t *testing.T) { + attribute := GenericAttribute{ + attributeDetails: attributeDetails{ + contentType: "contentType", + }, + } + + assert.Equal(t, attribute.ContentType(), "contentType") +} + +func TestNewGeneric_ShouldReturnNilForInvalidProtobuf(t *testing.T) { + invalid := NewGeneric(&yotiprotoattr.Attribute{ + ContentType: yotiprotoattr.ContentType_JSON, + }) + assert.Check(t, invalid == nil) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/helper_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/helper_test.go new file mode 100644 index 0000000..47c28ea --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/helper_test.go @@ -0,0 +1,21 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" +) + +func createAttributeFromTestFile(t *testing.T, filename string) *yotiprotoattr.Attribute { + attributeBytes := test.DecodeTestFile(t, filename) + + attributeStruct := &yotiprotoattr.Attribute{} + + err2 := proto.Unmarshal(attributeBytes, attributeStruct) + assert.NilError(t, err2) + + return attributeStruct +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute.go new file mode 100644 index 0000000..fd9d7f1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute.go @@ -0,0 +1,53 @@ +package attribute + +import ( + "errors" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// ImageAttribute is a Yoti attribute which returns an image as its value +type ImageAttribute struct { + attributeDetails + value media.Media +} + +// NewImage creates a new Image attribute +func NewImage(a *yotiprotoattr.Attribute) (*ImageAttribute, error) { + imageValue, err := parseImageValue(a.ContentType, a.Value) + if err != nil { + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &ImageAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: imageValue, + }, nil +} + +// Value returns the value of the ImageAttribute as media.Media +func (a *ImageAttribute) Value() media.Media { + return a.value +} + +func parseImageValue(contentType yotiprotoattr.ContentType, byteValue []byte) (media.Media, error) { + switch contentType { + case yotiprotoattr.ContentType_JPEG: + return media.JPEGImage(byteValue), nil + + case yotiprotoattr.ContentType_PNG: + return media.PNGImage(byteValue), nil + + default: + return nil, errors.New("cannot create Image with unsupported type") + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute_test.go new file mode 100644 index 0000000..2fe620f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_attribute_test.go @@ -0,0 +1,106 @@ +package attribute + +import ( + "encoding/base64" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestImageAttribute_Image_Png(t *testing.T) { + attributeName := consts.AttrSelfie + byteValue := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: byteValue, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + assert.DeepEqual(t, selfie.Value().Data(), byteValue) +} + +func TestImageAttribute_Image_Jpeg(t *testing.T) { + attributeName := consts.AttrSelfie + byteValue := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: byteValue, + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + assert.DeepEqual(t, selfie.Value().Data(), byteValue) +} + +func TestImageAttribute_Image_Default(t *testing.T) { + attributeName := consts.AttrSelfie + byteValue := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: byteValue, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + assert.DeepEqual(t, selfie.Value().Data(), byteValue) +} + +func TestImageAttribute_Base64Selfie_Png(t *testing.T) { + attributeName := consts.AttrSelfie + imageBytes := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: imageBytes, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(imageBytes) + + expectedBase64Selfie := "data:image/png;base64," + base64ImageExpectedValue + + base64Selfie := selfie.Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} + +func TestImageAttribute_Base64URL_Jpeg(t *testing.T) { + attributeName := consts.AttrSelfie + imageBytes := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: imageBytes, + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(imageBytes) + + expectedBase64Selfie := "data:image/jpeg;base64," + base64ImageExpectedValue + + base64Selfie := selfie.Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute.go new file mode 100644 index 0000000..de507ab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute.go @@ -0,0 +1,69 @@ +package attribute + +import ( + "errors" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// ImageSliceAttribute is a Yoti attribute which returns a slice of images as its value +type ImageSliceAttribute struct { + attributeDetails + value []media.Media +} + +// NewImageSlice creates a new ImageSlice attribute +func NewImageSlice(a *yotiprotoattr.Attribute) (*ImageSliceAttribute, error) { + if a.ContentType != yotiprotoattr.ContentType_MULTI_VALUE { + return nil, errors.New("creating an Image Slice attribute with content types other than MULTI_VALUE is not supported") + } + + parsedMultiValue, err := parseMultiValue(a.Value) + + if err != nil { + return nil, err + } + + var imageSliceValue []media.Media + if parsedMultiValue != nil { + imageSliceValue, err = CreateImageSlice(parsedMultiValue) + if err != nil { + return nil, err + } + } + + return &ImageSliceAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: anchor.ParseAnchors(a.Anchors), + id: &a.EphemeralId, + }, + value: imageSliceValue, + }, nil +} + +// CreateImageSlice takes a slice of Items, and converts them into a slice of images +func CreateImageSlice(items []*Item) (result []media.Media, err error) { + for _, item := range items { + + switch i := item.Value.(type) { + case media.PNGImage: + result = append(result, i) + case media.JPEGImage: + result = append(result, i) + default: + return nil, fmt.Errorf("unexpected item type %T", i) + } + } + + return result, nil +} + +// Value returns the value of the ImageSliceAttribute +func (a *ImageSliceAttribute) Value() []media.Media { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute_test.go new file mode 100644 index 0000000..2c30092 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/image_slice_attribute_test.go @@ -0,0 +1,61 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func assertIsExpectedImage(t *testing.T, image media.Media, imageMIMEType string, expectedBase64URLLast10 string) { + assert.Equal(t, image.MIME(), imageMIMEType) + + actualBase64URL := image.Base64URL() + + ActualBase64URLLast10Chars := actualBase64URL[len(actualBase64URL)-10:] + + assert.Equal(t, ActualBase64URLLast10Chars, expectedBase64URLLast10) +} + +func assertIsExpectedDocumentImagesAttribute(t *testing.T, actualDocumentImages []media.Media, anchor *anchor.Anchor) { + + assert.Equal(t, len(actualDocumentImages), 2, "This Document Images attribute should have two images") + + assertIsExpectedImage(t, actualDocumentImages[0], media.ImageTypeJPEG, "vWgD//2Q==") + assertIsExpectedImage(t, actualDocumentImages[1], media.ImageTypeJPEG, "38TVEH/9k=") + + expectedValue := "NATIONAL_ID" + assert.Equal(t, anchor.Value(), expectedValue) + + expectedSubType := "STATE_ID" + assert.Equal(t, anchor.SubType(), expectedSubType) +} + +func TestAttribute_NewImageSlice(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt") + + documentImagesAttribute, err := NewImageSlice(protoAttribute) + + assert.NilError(t, err) + + assertIsExpectedDocumentImagesAttribute(t, documentImagesAttribute.Value(), documentImagesAttribute.Anchors()[0]) +} + +func TestAttribute_ImageSliceNotCreatedWithNonMultiValueType(t *testing.T) { + attributeName := "attributeName" + attributeValueString := "value" + attributeValue := []byte(attributeValueString) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + _, err := NewImageSlice(attr) + + assert.Assert(t, err != nil, "Expected error when creating image slice from attribute which isn't of multi-value type") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details.go new file mode 100644 index 0000000..381d4e9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details.go @@ -0,0 +1,86 @@ +package attribute + +import ( + "encoding/base64" + "errors" + "fmt" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" +) + +// IssuanceDetails contains information about the attribute(s) issued by a third party +type IssuanceDetails struct { + token string + expiryDate *time.Time + attributes []Definition +} + +// Token is the issuance token that can be used to retrieve the user's stored details. +// These details will be used to issue attributes on behalf of an organisation to that user. +func (i IssuanceDetails) Token() string { + return i.token +} + +// ExpiryDate is the timestamp at which the request for the attribute value +// from third party will expire. Will be nil if not provided. +func (i IssuanceDetails) ExpiryDate() *time.Time { + return i.expiryDate +} + +// Attributes information about the attributes the third party would like to issue. +func (i IssuanceDetails) Attributes() []Definition { + return i.attributes +} + +// ParseIssuanceDetails takes the Third Party Attribute object and converts it into an IssuanceDetails struct +func ParseIssuanceDetails(thirdPartyAttributeBytes []byte) (*IssuanceDetails, error) { + thirdPartyAttributeStruct := &yotiprotoshare.ThirdPartyAttribute{} + if err := proto.Unmarshal(thirdPartyAttributeBytes, thirdPartyAttributeStruct); err != nil { + return nil, fmt.Errorf("unable to parse ThirdPartyAttribute value: %q. Error: %q", string(thirdPartyAttributeBytes), err) + } + + var issuingAttributesProto = thirdPartyAttributeStruct.GetIssuingAttributes() + var issuingAttributeDefinitions = parseIssuingAttributeDefinitions(issuingAttributesProto.GetDefinitions()) + + expiryDate, dateParseErr := parseExpiryDate(issuingAttributesProto.ExpiryDate) + + var issuanceTokenBytes = thirdPartyAttributeStruct.GetIssuanceToken() + + if len(issuanceTokenBytes) == 0 { + return nil, errors.New("Issuance Token is invalid") + } + + base64EncodedToken := base64.StdEncoding.EncodeToString(issuanceTokenBytes) + + return &IssuanceDetails{ + token: base64EncodedToken, + expiryDate: expiryDate, + attributes: issuingAttributeDefinitions, + }, dateParseErr +} + +func parseIssuingAttributeDefinitions(definitions []*yotiprotoshare.Definition) (issuingAttributes []Definition) { + for _, definition := range definitions { + attributeDefinition := Definition{ + name: definition.Name, + } + issuingAttributes = append(issuingAttributes, attributeDefinition) + } + + return issuingAttributes +} + +func parseExpiryDate(expiryDateString string) (*time.Time, error) { + if expiryDateString == "" { + return nil, nil + } + + parsedTime, err := time.Parse(time.RFC3339Nano, expiryDateString) + if err != nil { + return nil, err + } + + return &parsedTime, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details_test.go new file mode 100644 index 0000000..462d863 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/issuance_details_test.go @@ -0,0 +1,145 @@ +package attribute + +import ( + "encoding/base64" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" + + "gotest.tools/v3/assert" + + is "gotest.tools/v3/assert/cmp" +) + +func TestShouldParseThirdPartyAttributeCorrectly(t *testing.T) { + var thirdPartyAttributeBytes = test.GetTestFileBytes(t, "../../test/fixtures/test_third_party_issuance_details.txt") + issuanceDetails, err := ParseIssuanceDetails(thirdPartyAttributeBytes) + + assert.NilError(t, err) + assert.Equal(t, issuanceDetails.Attributes()[0].Name(), "com.thirdparty.id") + assert.Equal(t, issuanceDetails.Token(), "c29tZUlzc3VhbmNlVG9rZW4=") + assert.Equal(t, + issuanceDetails.ExpiryDate().Format("2006-01-02T15:04:05.000Z"), + "2019-10-15T22:04:05.123Z") +} + +func TestShouldLogWarningIfErrorInParsingExpiryDate(t *testing.T) { + var tokenValue = "41548a175dfaw" + thirdPartyAttribute := &yotiprotoshare.ThirdPartyAttribute{ + IssuanceToken: []byte(tokenValue), + IssuingAttributes: &yotiprotoshare.IssuingAttributes{ + ExpiryDate: "2006-13-02T15:04:05.000Z", + }, + } + + marshalled, err := proto.Marshal(thirdPartyAttribute) + + assert.NilError(t, err) + + var tokenBytes = []byte(tokenValue) + var expectedBase64Token = base64.StdEncoding.EncodeToString(tokenBytes) + + result, err := ParseIssuanceDetails(marshalled) + assert.Equal(t, expectedBase64Token, result.Token()) + assert.Assert(t, is.Nil(result.ExpiryDate())) + assert.Equal(t, "parsing time \"2006-13-02T15:04:05.000Z\": month out of range", err.Error()) +} + +func TestIssuanceDetails_parseExpiryDate_ShouldParseAllRFC3339Formats(t *testing.T) { + table := []struct { + Input string + Expected time.Time + }{ + { + Input: "2006-01-02T22:04:05Z", + Expected: time.Date(2006, 01, 02, 22, 4, 5, 0, time.UTC), + }, + { + Input: "2010-05-20T10:44:25Z", + Expected: time.Date(2010, 5, 20, 10, 44, 25, 0, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.1Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 100e6, time.UTC), + }, + { + Input: "2012-03-06T04:20:07.5Z", + Expected: time.Date(2012, 3, 6, 4, 20, 7, 500e6, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.12Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 120e6, time.UTC), + }, + { + Input: "2013-03-04T20:43:55.56Z", + Expected: time.Date(2013, 3, 4, 20, 43, 55, 560e6, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.123Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123e6, time.UTC), + }, + { + Input: "2007-04-07T17:34:11.784Z", + Expected: time.Date(2007, 4, 7, 17, 34, 11, 784e6, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.1234Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123400e3, time.UTC), + }, + { + Input: "2017-09-14T16:54:30.4784Z", + Expected: time.Date(2017, 9, 14, 16, 54, 30, 478400e3, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.12345Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123450e3, time.UTC), + }, + { + Input: "2009-06-07T14:20:30.74622Z", + Expected: time.Date(2009, 6, 7, 14, 20, 30, 746220e3, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.123456Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123456e3, time.UTC), + }, + { + Input: "2008-10-25T06:50:55.643562Z", + Expected: time.Date(2008, 10, 25, 6, 50, 55, 643562e3, time.UTC), + }, + { + Input: "2002-10-02T10:00:00-05:00", + Expected: time.Date(2002, 10, 2, 10, 0, 0, 0, time.FixedZone("-0500", -5*60*60)), + }, + { + Input: "2002-10-02T10:00:00+11:00", + Expected: time.Date(2002, 10, 2, 10, 0, 0, 0, time.FixedZone("+1100", 11*60*60)), + }, + { + Input: "1920-03-13T19:50:53.999999Z", + Expected: time.Date(1920, 3, 13, 19, 50, 53, 999999e3, time.UTC), + }, + { + Input: "1920-03-13T19:50:54.000001Z", + Expected: time.Date(1920, 3, 13, 19, 50, 54, 1e3, time.UTC), + }, + } + + for _, row := range table { + func(input string, expected time.Time) { + expiryDate, err := parseExpiryDate(input) + assert.NilError(t, err) + assert.Equal(t, expiryDate.UTC(), expected.UTC()) + }(row.Input, row.Expected) + } +} + +func TestInvalidProtobufThrowsError(t *testing.T) { + result, err := ParseIssuanceDetails([]byte("invalid")) + + assert.Assert(t, is.Nil(result)) + + assert.ErrorContains(t, err, "unable to parse ThirdPartyAttribute value") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/item.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/item.go new file mode 100644 index 0000000..3efd2b9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/item.go @@ -0,0 +1,14 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// Item is a structure which contains information about an attribute value +type Item struct { + // ContentType is the content of the item. + ContentType yotiprotoattr.ContentType + + // Value is the underlying data of the item. + Value interface{} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute.go new file mode 100644 index 0000000..be40920 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute.go @@ -0,0 +1,58 @@ +package attribute + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// JSONAttribute is a Yoti attribute which returns an interface as its value +type JSONAttribute struct { + attributeDetails + // value returns the value of a JSON attribute in the form of an interface + value map[string]interface{} +} + +// NewJSON creates a new JSON attribute +func NewJSON(a *yotiprotoattr.Attribute) (*JSONAttribute, error) { + var interfaceValue map[string]interface{} + decoder := json.NewDecoder(bytes.NewReader(a.Value)) + decoder.UseNumber() + err := decoder.Decode(&interfaceValue) + if err != nil { + err = fmt.Errorf("unable to parse JSON value: %q. Error: %q", a.Value, err) + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &JSONAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: interfaceValue, + }, nil +} + +// unmarshallJSON unmarshalls JSON into an interface +func unmarshallJSON(byteValue []byte) (result map[string]interface{}, err error) { + var unmarshalledJSON map[string]interface{} + err = json.Unmarshal(byteValue, &unmarshalledJSON) + + if err != nil { + return nil, err + } + + return unmarshalledJSON, err +} + +// Value returns the value of the JSONAttribute as an interface. +func (a *JSONAttribute) Value() map[string]interface{} { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute_test.go new file mode 100644 index 0000000..4275637 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/json_attribute_test.go @@ -0,0 +1,76 @@ +package attribute + +import ( + "fmt" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func ExampleNewJSON() { + proto := yotiprotoattr.Attribute{ + Name: "exampleJSON", + Value: []byte(`{"foo":"bar"}`), + ContentType: yotiprotoattr.ContentType_JSON, + } + attribute, err := NewJSON(&proto) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + fmt.Println(attribute.Value()) + // Output: map[foo:bar] +} + +func TestNewJSON_ShouldReturnNilForInvalidJSON(t *testing.T) { + proto := yotiprotoattr.Attribute{ + Name: "exampleJSON", + Value: []byte("Not a json document"), + ContentType: yotiprotoattr.ContentType_JSON, + } + attribute, err := NewJSON(&proto) + assert.Check(t, attribute == nil) + assert.ErrorContains(t, err, "unable to parse JSON value") +} + +func TestYotiClient_UnmarshallJSONValue_InvalidValueThrowsError(t *testing.T) { + invalidStructuredAddress := []byte("invalidBool") + + _, err := unmarshallJSON(invalidStructuredAddress) + + assert.Assert(t, err != nil) +} + +func TestYotiClient_UnmarshallJSONValue_ValidValue(t *testing.T) { + const ( + countryIso = "IND" + nestedValue = "NestedValue" + ) + + var structuredAddress = []byte(` + { + "address_format": 2, + "building": "House No.86-A", + "state": "Punjab", + "postal_code": "141012", + "country_iso": "` + countryIso + `", + "country": "India", + "formatted_address": "House No.86-A\nRajgura Nagar\nLudhina\nPunjab\n141012\nIndia", + "1": + { + "1-1": + { + "1-1-1": "` + nestedValue + `" + } + } + } + `) + + parsedStructuredAddress, err := unmarshallJSON(structuredAddress) + assert.NilError(t, err, "Failed to parse structured address") + + actualCountryIso := parsedStructuredAddress["country_iso"] + + assert.Equal(t, countryIso, actualCountryIso) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute.go new file mode 100644 index 0000000..926141f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute.go @@ -0,0 +1,90 @@ +package attribute + +import ( + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" +) + +// MultiValueAttribute is a Yoti attribute which returns a multi-valued attribute +type MultiValueAttribute struct { + attributeDetails + items []*Item +} + +// NewMultiValue creates a new MultiValue attribute +func NewMultiValue(a *yotiprotoattr.Attribute) (*MultiValueAttribute, error) { + attributeItems, err := parseMultiValue(a.Value) + + if err != nil { + return nil, err + } + + return &MultiValueAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: anchor.ParseAnchors(a.Anchors), + id: &a.EphemeralId, + }, + items: attributeItems, + }, nil +} + +// parseMultiValue recursively unmarshals and converts Multi Value bytes into a slice of Items +func parseMultiValue(data []byte) ([]*Item, error) { + var attributeItems []*Item + protoMultiValueStruct, err := unmarshallMultiValue(data) + + if err != nil { + return nil, err + } + + for _, multiValueItem := range protoMultiValueStruct.Values { + var value *Item + if multiValueItem.ContentType == yotiprotoattr.ContentType_MULTI_VALUE { + parsedInnerMultiValueItems, err := parseMultiValue(multiValueItem.Data) + + if err != nil { + return nil, fmt.Errorf("unable to parse multi-value data: %v", err) + } + + value = &Item{ + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Value: parsedInnerMultiValueItems, + } + } else { + itemValue, err := parseValue(multiValueItem.ContentType, multiValueItem.Data) + + if err != nil { + return nil, fmt.Errorf("unable to parse data within a multi-value attribute. Content type: %q, data: %q, error: %v", + multiValueItem.ContentType, multiValueItem.Data, err) + } + + value = &Item{ + ContentType: multiValueItem.ContentType, + Value: itemValue, + } + } + attributeItems = append(attributeItems, value) + } + + return attributeItems, nil +} + +func unmarshallMultiValue(bytes []byte) (*yotiprotoattr.MultiValue, error) { + multiValueStruct := &yotiprotoattr.MultiValue{} + + if err := proto.Unmarshal(bytes, multiValueStruct); err != nil { + return nil, fmt.Errorf("unable to parse MULTI_VALUE value: %q. Error: %q", string(bytes), err) + } + + return multiValueStruct, nil +} + +// Value returns the value of the MultiValueAttribute as a string +func (a *MultiValueAttribute) Value() []*Item { + return a.items +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute_test.go new file mode 100644 index 0000000..15a24f9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/multivalue_attribute_test.go @@ -0,0 +1,157 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func marshallMultiValue(t *testing.T, multiValue *yotiprotoattr.MultiValue) []byte { + marshalled, err := proto.Marshal(multiValue) + + assert.NilError(t, err) + + return marshalled +} + +func createMultiValueAttribute(t *testing.T, multiValueItemSlice []*yotiprotoattr.MultiValue_Value) (*MultiValueAttribute, error) { + var multiValueStruct = &yotiprotoattr.MultiValue{ + Values: multiValueItemSlice, + } + + var marshalledMultiValueData = marshallMultiValue(t, multiValueStruct) + attributeName := "nestedMultiValue" + + var protoAttribute = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: marshalledMultiValueData, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: []*yotiprotoattr.Anchor{}, + } + + return NewMultiValue(protoAttribute) +} + +func TestAttribute_MultiValueNotCreatedWithNonMultiValueType(t *testing.T) { + attributeName := "attributeName" + attributeValueString := "value" + attributeValue := []byte(attributeValueString) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + _, err := NewMultiValue(attr) + + assert.Assert(t, err != nil, "Expected error when creating multi value from attribute which isn't of multi-value type") +} + +func TestAttribute_NewMultiValue(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt") + + multiValueAttribute, err := NewMultiValue(protoAttribute) + + assert.NilError(t, err) + + documentImagesAttributeItems, err := CreateImageSlice(multiValueAttribute.Value()) + assert.NilError(t, err) + + assertIsExpectedDocumentImagesAttribute(t, documentImagesAttributeItems, multiValueAttribute.Anchors()[0]) +} + +func TestAttribute_InvalidMultiValueNotReturned(t *testing.T) { + var invalidMultiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_DATE, + Data: []byte("invalid"), + } + + var stringMultiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_STRING, + Data: []byte("string"), + } + + var multiValueItemSlice = []*yotiprotoattr.MultiValue_Value{invalidMultiValueItem, stringMultiValueItem} + + var multiValueStruct = &yotiprotoattr.MultiValue{ + Values: multiValueItemSlice, + } + + var marshalledMultiValueData = marshallMultiValue(t, multiValueStruct) + attributeName := "nestedMultiValue" + + var protoAttribute = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: marshalledMultiValueData, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: []*yotiprotoattr.Anchor{}, + } + + multiValueAttr, err := NewMultiValue(protoAttribute) + assert.Check(t, err != nil) + + assert.Assert(t, is.Nil(multiValueAttr)) +} + +func TestAttribute_NestedMultiValue(t *testing.T) { + var innerMultiValueProtoValue = createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt").Value + + var stringMultiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_STRING, + Data: []byte("string"), + } + + var multiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Data: innerMultiValueProtoValue, + } + + var multiValueItemSlice = []*yotiprotoattr.MultiValue_Value{stringMultiValueItem, multiValueItem} + + multiValueAttribute, err := createMultiValueAttribute(t, multiValueItemSlice) + + assert.NilError(t, err) + + for key, value := range multiValueAttribute.Value() { + switch key { + case 0: + value0 := value.Value + + assert.Equal(t, value0.(string), "string") + case 1: + value1 := value.Value + + innerItems, ok := value1.([]*Item) + assert.Assert(t, ok) + + for innerKey, item := range innerItems { + switch innerKey { + case 0: + assertIsExpectedImage(t, item.Value.(media.Media), media.ImageTypeJPEG, "vWgD//2Q==") + + case 1: + assertIsExpectedImage(t, item.Value.(media.Media), media.ImageTypeJPEG, "38TVEH/9k=") + } + } + } + } +} + +func TestAttribute_MultiValueGenericGetter(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt") + multiValueAttribute, err := NewMultiValue(protoAttribute) + assert.NilError(t, err) + + // We need to cast, since GetAttribute always returns generic attributes + multiValueAttributeValue := multiValueAttribute.Value() + imageSlice, err := CreateImageSlice(multiValueAttributeValue) + assert.NilError(t, err) + + assertIsExpectedDocumentImagesAttribute(t, imageSlice, multiValueAttribute.Anchors()[0]) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser.go new file mode 100644 index 0000000..d663595 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser.go @@ -0,0 +1,56 @@ +package attribute + +import ( + "fmt" + "strconv" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +func parseValue(contentType yotiprotoattr.ContentType, byteValue []byte) (interface{}, error) { + switch contentType { + case yotiprotoattr.ContentType_DATE: + parsedTime, err := time.Parse("2006-01-02", string(byteValue)) + + if err == nil { + return &parsedTime, nil + } + + return nil, fmt.Errorf("unable to parse date value: %q. Error: %q", string(byteValue), err) + + case yotiprotoattr.ContentType_JSON: + unmarshalledJSON, err := unmarshallJSON(byteValue) + + if err == nil { + return unmarshalledJSON, nil + } + + return nil, fmt.Errorf("unable to parse JSON value: %q. Error: %q", string(byteValue), err) + + case yotiprotoattr.ContentType_STRING: + return string(byteValue), nil + + case yotiprotoattr.ContentType_MULTI_VALUE: + return parseMultiValue(byteValue) + + case yotiprotoattr.ContentType_INT: + var stringValue = string(byteValue) + intValue, err := strconv.Atoi(stringValue) + if err == nil { + return intValue, nil + } + + return nil, fmt.Errorf("unable to parse INT value: %q. Error: %q", string(byteValue), err) + + case yotiprotoattr.ContentType_JPEG, + yotiprotoattr.ContentType_PNG: + return parseImageValue(contentType, byteValue) + + case yotiprotoattr.ContentType_UNDEFINED: + return string(byteValue), nil + + default: + return string(byteValue), nil + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser_test.go new file mode 100644 index 0000000..cc9f3d8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/parser_test.go @@ -0,0 +1,16 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestParseValue_ShouldParseInt(t *testing.T) { + parsed, err := parseValue(yotiprotoattr.ContentType_INT, []byte("7")) + assert.NilError(t, err) + integer, ok := parsed.(int) + assert.Check(t, ok) + assert.Equal(t, integer, 7) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute.go new file mode 100644 index 0000000..73346b9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute.go @@ -0,0 +1,32 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// StringAttribute is a Yoti attribute which returns a string as its value +type StringAttribute struct { + attributeDetails + value string +} + +// NewString creates a new String attribute +func NewString(a *yotiprotoattr.Attribute) *StringAttribute { + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &StringAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: string(a.Value), + } +} + +// Value returns the value of the StringAttribute as a string +func (a *StringAttribute) Value() string { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute_test.go new file mode 100644 index 0000000..828df20 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/attribute/string_attribute_test.go @@ -0,0 +1,22 @@ +package attribute + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestStringAttribute_NewThirdPartyAttribute(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_third_party.txt") + + stringAttribute := NewString(protoAttribute) + + assert.Equal(t, stringAttribute.Value(), "test-third-party-attribute-0") + assert.Equal(t, stringAttribute.Name(), "com.thirdparty.id") + + assert.Equal(t, stringAttribute.Sources()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Sources()[0].SubType(), "orgName") + + assert.Equal(t, stringAttribute.Verifiers()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Verifiers()[0].SubType(), "orgName") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/base_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/base_profile.go new file mode 100644 index 0000000..693441d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/base_profile.go @@ -0,0 +1,75 @@ +package digitalidentity + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +type baseProfile struct { + attributeSlice []*yotiprotoattr.Attribute +} + +// GetAttribute retrieve an attribute by name on the Yoti profile. Will return nil if attribute is not present. +func (p baseProfile) GetAttribute(attributeName string) *attribute.GenericAttribute { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + return attribute.NewGeneric(a) + } + } + return nil +} + +// GetAttributeByID retrieve an attribute by ID on the Yoti profile. Will return nil if attribute is not present. +func (p baseProfile) GetAttributeByID(attributeID string) *attribute.GenericAttribute { + for _, a := range p.attributeSlice { + if a.EphemeralId == attributeID { + return attribute.NewGeneric(a) + } + } + return nil +} + +// GetAttributes retrieve a list of attributes by name on the Yoti profile. Will return an empty list of attribute is not present. +func (p baseProfile) GetAttributes(attributeName string) []*attribute.GenericAttribute { + var attributes []*attribute.GenericAttribute + for _, a := range p.attributeSlice { + if a.Name == attributeName { + attributes = append(attributes, attribute.NewGeneric(a)) + } + } + return attributes +} + +// GetStringAttribute retrieves a string attribute by name. Will return nil if attribute is not present. +func (p baseProfile) GetStringAttribute(attributeName string) *attribute.StringAttribute { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + return attribute.NewString(a) + } + } + return nil +} + +// GetImageAttribute retrieves an image attribute by name. Will return nil if attribute is not present. +func (p baseProfile) GetImageAttribute(attributeName string) *attribute.ImageAttribute { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + imageAttribute, err := attribute.NewImage(a) + + if err == nil { + return imageAttribute + } + } + } + return nil +} + +// GetJSONAttribute retrieves a JSON attribute by name. Will return nil if attribute is not present. +func (p baseProfile) GetJSONAttribute(attributeName string) (*attribute.JSONAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + return attribute.NewJSON(a) + } + } + return nil, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder.go new file mode 100644 index 0000000..4dec515 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder.go @@ -0,0 +1,261 @@ +package digitalidentity + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +const ( + authTypeSelfieConst = 1 + authTypePinConst = 2 +) + +// PolicyBuilder constructs a json payload specifying the dynamic policy +// for a dynamic scenario +type PolicyBuilder struct { + wantedAttributes map[string]WantedAttribute + wantedAuthTypes map[int]bool + isWantedRememberMe bool + err error + identityProfileRequirements *json.RawMessage + advancedIdentityProfileRequirements *json.RawMessage +} + +// Policy represents a dynamic policy for a share +type Policy struct { + attributes []WantedAttribute + authTypes []int + rememberMeID bool + identityProfileRequirements *json.RawMessage + advancedIdentityProfileRequirements *json.RawMessage +} + +// WithWantedAttribute adds an attribute from WantedAttributeBuilder to the policy +func (b *PolicyBuilder) WithWantedAttribute(attribute WantedAttribute) *PolicyBuilder { + if b.wantedAttributes == nil { + b.wantedAttributes = make(map[string]WantedAttribute) + } + var key string + if attribute.derivation != "" { + key = attribute.derivation + } else { + key = attribute.name + } + b.wantedAttributes[key] = attribute + return b +} + +// WithWantedAttributeByName adds an attribute by its name. This is not the preferred +// way of adding an attribute - instead use the other methods below. +// Options allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithWantedAttributeByName(name string, options ...interface{}) *PolicyBuilder { + attributeBuilder := (&WantedAttributeBuilder{}).WithName(name) + + for _, option := range options { + switch value := option.(type) { + case SourceConstraint: + attributeBuilder.WithConstraint(&value) + case constraintInterface: + attributeBuilder.WithConstraint(value) + default: + panic(fmt.Sprintf("not a valid option type, %v", value)) + } + } + + attribute, err := attributeBuilder.Build() + if err != nil { + b.err = yotierror.MultiError{This: err, Next: b.err} + } + b.WithWantedAttribute(attribute) + return b +} + +// WithFamilyName adds the family name attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithFamilyName(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrFamilyName, options...) +} + +// WithGivenNames adds the given names attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithGivenNames(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrGivenNames, options...) +} + +// WithFullName adds the full name attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithFullName(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrFullName, options...) +} + +// WithDateOfBirth adds the date of birth attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithDateOfBirth(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrDateOfBirth, options...) +} + +// WithGender adds the gender attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithGender(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrGender, options...) +} + +// WithPostalAddress adds the postal address attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithPostalAddress(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrAddress, options...) +} + +// WithStructuredPostalAddress adds the structured postal address attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithStructuredPostalAddress(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrStructuredPostalAddress, options...) +} + +// WithNationality adds the nationality attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithNationality(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrNationality, options...) +} + +// WithPhoneNumber adds the phone number attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithPhoneNumber(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrMobileNumber, options...) +} + +// WithSelfie adds the selfie attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithSelfie(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrSelfie, options...) +} + +// WithEmail adds the email address attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithEmail(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrEmailAddress, options...) +} + +// WithDocumentImages adds the document images attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithDocumentImages(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrDocumentImages, options...) +} + +// WithDocumentDetails adds the document details attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithDocumentDetails(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrDocumentDetails, options...) +} + +// WithAgeDerivedAttribute is a helper method for setting age based derivations +// Prefer to use WithAgeOver and WithAgeUnder instead of using this directly. +// "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithAgeDerivedAttribute(derivation string, options ...interface{}) *PolicyBuilder { + var attributeBuilder WantedAttributeBuilder + attributeBuilder. + WithName(consts.AttrDateOfBirth). + WithDerivation(derivation) + + for _, option := range options { + switch value := option.(type) { + case SourceConstraint: + attributeBuilder.WithConstraint(&value) + case constraintInterface: + attributeBuilder.WithConstraint(value) + default: + panic(fmt.Sprintf("not a valid option type, %v", value)) + } + } + + attr, err := attributeBuilder.Build() + if err != nil { + b.err = yotierror.MultiError{This: err, Next: b.err} + } + return b.WithWantedAttribute(attr) +} + +// WithAgeOver sets this dynamic policy as requesting whether the user is older than a certain age. +// "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithAgeOver(age int, options ...interface{}) *PolicyBuilder { + return b.WithAgeDerivedAttribute(fmt.Sprintf(consts.AttrAgeOver, age), options...) +} + +// WithAgeUnder sets this dynamic policy as requesting whether the user is younger +// than a certain age, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithAgeUnder(age int, options ...interface{}) *PolicyBuilder { + return b.WithAgeDerivedAttribute(fmt.Sprintf(consts.AttrAgeUnder, age), options...) +} + +// WithWantedRememberMe sets the Policy as requiring a "Remember Me ID" +func (b *PolicyBuilder) WithWantedRememberMe() *PolicyBuilder { + b.isWantedRememberMe = true + return b +} + +// WithWantedAuthType sets this dynamic policy as requiring a specific authentication type +func (b *PolicyBuilder) WithWantedAuthType(wantedAuthType int) *PolicyBuilder { + if b.wantedAuthTypes == nil { + b.wantedAuthTypes = make(map[int]bool) + } + b.wantedAuthTypes[wantedAuthType] = true + return b +} + +// WithSelfieAuth sets this dynamic policy as requiring Selfie-based authentication +func (b *PolicyBuilder) WithSelfieAuth() *PolicyBuilder { + return b.WithWantedAuthType(authTypeSelfieConst) +} + +// WithPinAuth sets this dynamic policy as requiring PIN authentication +func (b *PolicyBuilder) WithPinAuth() *PolicyBuilder { + return b.WithWantedAuthType(authTypePinConst) +} + +// WithIdentityProfileRequirements adds Identity Profile Requirements to the policy. Must be valid JSON. +func (b *PolicyBuilder) WithIdentityProfileRequirements(identityProfile json.RawMessage) *PolicyBuilder { + b.identityProfileRequirements = &identityProfile + return b +} + +// WithAdvancedIdentityProfileRequirements adds Advanced Identity Profile Requirements to the policy. Must be valid JSON. +func (b *PolicyBuilder) WithAdvancedIdentityProfileRequirements(advancedIdentityProfile json.RawMessage) *PolicyBuilder { + b.advancedIdentityProfileRequirements = &advancedIdentityProfile + return b +} + +// Build constructs a dynamic policy object +func (b *PolicyBuilder) Build() (Policy, error) { + return Policy{ + attributes: b.attributesAsList(), + authTypes: b.authTypesAsList(), + rememberMeID: b.isWantedRememberMe, + identityProfileRequirements: b.identityProfileRequirements, + advancedIdentityProfileRequirements: b.advancedIdentityProfileRequirements, + }, b.err +} + +func (b *PolicyBuilder) attributesAsList() []WantedAttribute { + attributeList := make([]WantedAttribute, 0) + for _, attr := range b.wantedAttributes { + attributeList = append(attributeList, attr) + } + return attributeList +} + +func (b *PolicyBuilder) authTypesAsList() []int { + authTypeList := make([]int, 0) + for auth, boolValue := range b.wantedAuthTypes { + if boolValue { + authTypeList = append(authTypeList, auth) + } + } + return authTypeList +} + +// MarshalJSON returns the JSON encoding +func (policy *Policy) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Wanted []WantedAttribute `json:"wanted"` + WantedAuthTypes []int `json:"wanted_auth_types"` + WantedRememberMe bool `json:"wanted_remember_me"` + IdentityProfileRequirements *json.RawMessage `json:"identity_profile_requirements,omitempty"` + AdvancedIdentityProfileRequirements *json.RawMessage `json:"advanced_identity_profile_requirements,omitempty"` + }{ + Wanted: policy.attributes, + WantedAuthTypes: policy.authTypes, + WantedRememberMe: policy.rememberMeID, + IdentityProfileRequirements: policy.identityProfileRequirements, + AdvancedIdentityProfileRequirements: policy.advancedIdentityProfileRequirements, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder_test.go new file mode 100644 index 0000000..a7ae6e1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/policy_builder_test.go @@ -0,0 +1,508 @@ +package digitalidentity + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" + "gotest.tools/v3/assert" +) + +func ExamplePolicyBuilder_WithFamilyName() { + policy, err := (&PolicyBuilder{}).WithFamilyName().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"family_name","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithDocumentDetails() { + policy, err := (&PolicyBuilder{}).WithDocumentDetails().Build() + if err != nil { + return + } + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"document_details","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithDocumentImages() { + policy, err := (&PolicyBuilder{}).WithDocumentImages().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"document_images","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithSelfie() { + policy, err := (&PolicyBuilder{}).WithSelfie().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"selfie","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithAgeOver() { + constraint, err := (&SourceConstraintBuilder{}).WithDrivingLicence("").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + policy, err := (&PolicyBuilder{}).WithAgeOver(18, constraint).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"date_of_birth","derivation":"age_over:18","constraints":[{"type":"SOURCE","preferred_sources":{"anchors":[{"name":"DRIVING_LICENCE","sub_type":""}],"soft_preference":false}}],"accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithSelfieAuth() { + policy, err := (&PolicyBuilder{}).WithSelfieAuth().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[1],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithWantedRememberMe() { + policy, err := (&PolicyBuilder{}).WithWantedRememberMe().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[],"wanted_remember_me":true} +} + +func ExamplePolicyBuilder_WithFullName() { + constraint, err := (&SourceConstraintBuilder{}).WithPassport("").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + policy, err := (&PolicyBuilder{}).WithFullName(&constraint).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"wanted":[{"name":"full_name","constraints":[{"type":"SOURCE","preferred_sources":{"anchors":[{"name":"PASSPORT","sub_type":""}],"soft_preference":false}}],"accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder() { + policy, err := (&PolicyBuilder{}).WithFullName(). + WithPinAuth().WithWantedRememberMe().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"full_name","accept_self_asserted":false}],"wanted_auth_types":[2],"wanted_remember_me":true} +} + +func ExamplePolicyBuilder_WithAgeUnder() { + policy, err := (&PolicyBuilder{}).WithAgeUnder(18).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"date_of_birth","derivation":"age_under:18","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithGivenNames() { + policy, err := (&PolicyBuilder{}).WithGivenNames().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"given_names","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithDateOfBirth() { + policy, err := (&PolicyBuilder{}).WithDateOfBirth().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"date_of_birth","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithGender() { + policy, err := (&PolicyBuilder{}).WithGender().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"gender","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithPostalAddress() { + policy, err := (&PolicyBuilder{}).WithPostalAddress().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"postal_address","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithStructuredPostalAddress() { + policy, err := (&PolicyBuilder{}).WithStructuredPostalAddress().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"structured_postal_address","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithNationality() { + policy, err := (&PolicyBuilder{}).WithNationality().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"nationality","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithPhoneNumber() { + policy, err := (&PolicyBuilder{}).WithPhoneNumber().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"phone_number","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func TestDigitalIdentityBuilder_WithWantedAttributeByName_WithSourceConstraint(t *testing.T) { + attributeName := "attributeName" + builder := &PolicyBuilder{} + sourceConstraint, err := (&SourceConstraintBuilder{}).Build() + assert.NilError(t, err) + + builder.WithWantedAttributeByName( + attributeName, + sourceConstraint, + ) + + policy, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, len(policy.attributes), 1) + assert.Equal(t, policy.attributes[0].name, attributeName) + assert.Equal(t, len(policy.attributes[0].constraints), 1) +} + +func TestDigitalIdentityBuilder_WithWantedAttributeByName_InvalidOptionsShouldPanic(t *testing.T) { + attributeName := "attributeName" + builder := &PolicyBuilder{} + invalidOption := "invalidOption" + + defer func() { + r := recover().(string) + assert.Check(t, strings.Contains(r, "not a valid option type")) + }() + + builder.WithWantedAttributeByName( + attributeName, + invalidOption, + ) + + t.Error("Expected Panic") + +} + +func TestDigitalIdentityBuilder_WithWantedAttributeByName_ShouldPropagateErrors(t *testing.T) { + builder := &PolicyBuilder{} + + builder.WithWantedAttributeByName("") + builder.WithWantedAttributeByName("") + + _, err := builder.Build() + + assert.Error(t, err, "wanted attribute names must not be empty, wanted attribute names must not be empty") + assert.Error(t, err.(yotierror.MultiError).Unwrap(), "wanted attribute names must not be empty") +} + +func TestDigitalIdentityBuilder_WithAgeDerivedAttribute_WithSourceConstraint(t *testing.T) { + builder := &PolicyBuilder{} + sourceConstraint, err := (&SourceConstraintBuilder{}).Build() + assert.NilError(t, err) + + builder.WithAgeDerivedAttribute( + fmt.Sprintf(consts.AttrAgeOver, 18), + sourceConstraint, + ) + + policy, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, len(policy.attributes), 1) + assert.Equal(t, policy.attributes[0].name, consts.AttrDateOfBirth) + assert.Equal(t, len(policy.attributes[0].constraints), 1) +} + +func TestDigitalIdentityBuilder_WithAgeDerivedAttribute_WithConstraintInterface(t *testing.T) { + builder := &PolicyBuilder{} + var constraint constraintInterface + sourceConstraint, err := (&SourceConstraintBuilder{}).Build() + constraint = &sourceConstraint + assert.NilError(t, err) + + builder.WithAgeDerivedAttribute( + fmt.Sprintf(consts.AttrAgeOver, 18), + constraint, + ) + + policy, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, len(policy.attributes), 1) + assert.Equal(t, policy.attributes[0].name, consts.AttrDateOfBirth) + assert.Equal(t, len(policy.attributes[0].constraints), 1) +} + +func TestDigitalIdentityBuilder_WithAgeDerivedAttribute_InvalidOptionsShouldPanic(t *testing.T) { + builder := &PolicyBuilder{} + invalidOption := "invalidOption" + + defer func() { + r := recover().(string) + assert.Check(t, strings.Contains(r, "not a valid option type")) + }() + + builder.WithAgeDerivedAttribute( + fmt.Sprintf(consts.AttrAgeOver, 18), + invalidOption, + ) + + t.Error("Expected Panic") + +} + +func TestDigitalIdentityBuilder_WithIdentityProfileRequirements_ShouldFailForInvalidJSON(t *testing.T) { + identityProfile := []byte(`{ + "trust_framework": UK_TFIDA", + , + }`) + + policy, err := (&PolicyBuilder{}).WithIdentityProfileRequirements(identityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + _, err = policy.MarshalJSON() + if err == nil { + t.Error("expected an error") + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} + +func ExamplePolicyBuilder_WithAdvancedIdentityProfileRequirements() { + advancedIdentityProfile := []byte(`{ + "profiles": [ + { + "trust_framework": "UK_TFIDA", + "schemes": [ + { + "label": "LB912", + "type": "RTW" + }, + { + "label": "LB777", + "type": "DBS", + "objective": "BASIC" + } + ] + }, + { + "trust_framework": "YOTI_GLOBAL", + "schemes": [ + { + "label": "LB321", + "type": "IDENTITY", + "objective": "AL_L1", + "config": {} + } + ] + } + ] + }`) + + policy, err := (&PolicyBuilder{}).WithAdvancedIdentityProfileRequirements(advancedIdentityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[],"wanted_remember_me":false,"advanced_identity_profile_requirements":{"profiles":[{"trust_framework":"UK_TFIDA","schemes":[{"label":"LB912","type":"RTW"},{"label":"LB777","type":"DBS","objective":"BASIC"}]},{"trust_framework":"YOTI_GLOBAL","schemes":[{"label":"LB321","type":"IDENTITY","objective":"AL_L1","config":{}}]}]}} +} + +func TestPolicyBuilder_WithAdvancedIdentityProfileRequirements_ShouldFailForInvalidJSON(t *testing.T) { + advancedIdentityProfile := []byte(`{ + "trust_framework": UK_TFIDA", + , + }`) + + policy, err := (&PolicyBuilder{}).WithAdvancedIdentityProfileRequirements(advancedIdentityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + _, err = policy.MarshalJSON() + if err == nil { + t.Error("expected an error") + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/qr_code.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/qr_code.go new file mode 100644 index 0000000..bff20b6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/qr_code.go @@ -0,0 +1,6 @@ +package digitalidentity + +type QrCode struct { + Id string `json:"id"` + Uri string `json:"uri"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt.go new file mode 100644 index 0000000..3c39c9b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt.go @@ -0,0 +1,32 @@ +package digitalidentity + +type Content struct { + Profile []byte `json:"profile"` + ExtraData []byte `json:"extraData"` +} + +type RequirementsNotMetDetail struct { + Details string `json:"details"` + AuditId string `json:"audit_id"` + FailureType string `json:"failure_type"` + DocumentType string `json:"document_type"` + DocumentCountryIsoCode string `json:"document_country_iso_code"` +} + +type ErrorReason struct { + RequirementsNotMetDetails []RequirementsNotMetDetail `json:"requirements_not_met_details"` +} + +type ReceiptResponse struct { + ID string `json:"id"` + SessionID string `json:"sessionId"` + Timestamp string `json:"timestamp"` + RememberMeID string `json:"rememberMeId,omitempty"` + ParentRememberMeID string `json:"parentRememberMeId,omitempty"` + Content *Content `json:"content,omitempty"` + OtherPartyContent *Content `json:"otherPartyContent,omitempty"` + WrappedItemKeyId string `json:"wrappedItemKeyId"` + WrappedKey []byte `json:"wrappedKey"` + Error string `json:"error"` + ErrorReason ErrorReason `json:"errorReason"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt_item_key.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt_item_key.go new file mode 100644 index 0000000..7e805d8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/receipt_item_key.go @@ -0,0 +1,7 @@ +package digitalidentity + +type ReceiptItemKeyResponse struct { + ID string `json:"id"` + Iv []byte `json:"iv"` + Value []byte `json:"value"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/client.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/client.go new file mode 100644 index 0000000..74c289e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/client.go @@ -0,0 +1,10 @@ +package requests + +import ( + "net/http" +) + +// HttpClient is a mockable HTTP Client Interface +type HttpClient interface { + Do(*http.Request) (*http.Response, error) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request.go new file mode 100644 index 0000000..e5bbeab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request.go @@ -0,0 +1,40 @@ +package requests + +import ( + "net/http" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/digitalidentity/yotierror" +) + +// Execute makes a request to the specified endpoint, with an optional payload +func Execute(httpClient HttpClient, request *http.Request) (response *http.Response, err error) { + + if response, err = doRequest(request, httpClient); err != nil { + + return + } + + statusCodeIsFailure := response.StatusCode >= 300 || response.StatusCode < 200 + + if statusCodeIsFailure { + return response, yotierror.NewResponseError(response) + } + + return response, nil +} + +func doRequest(request *http.Request, httpClient HttpClient) (*http.Response, error) { + httpClient = ensureHttpClientTimeout(httpClient) + return httpClient.Do(request) +} + +func ensureHttpClientTimeout(httpClient HttpClient) HttpClient { + if httpClient == nil { + httpClient = &http.Client{ + Timeout: time.Second * 10, + } + } + + return httpClient +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request_test.go new file mode 100644 index 0000000..420fa6b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/request_test.go @@ -0,0 +1,71 @@ +package requests + +import ( + "net/http" + "testing" + "time" + + "gotest.tools/v3/assert" +) + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func TestExecute_Success(t *testing.T) { + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + }, nil + }, + } + + request := &http.Request{ + Method: http.MethodGet, + } + + response, err := Execute(client, request) + + assert.NilError(t, err) + assert.Equal(t, response.StatusCode, 200) +} + +func TestExecute_Failure(t *testing.T) { + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 400, + }, nil + }, + } + + request := &http.Request{ + Method: http.MethodGet, + } + + _, err := Execute(client, request) + assert.ErrorContains(t, err, "unknown HTTP error") +} + +func TestEnsureHttpClientTimeout_NilHTTPClientShouldUse10sTimeout(t *testing.T) { + result := ensureHttpClientTimeout(nil).(*http.Client) + + assert.Equal(t, 10*time.Second, result.Timeout) +} + +func TestEnsureHttpClientTimeout(t *testing.T) { + httpClient := &http.Client{ + Timeout: time.Minute * 12, + } + result := ensureHttpClientTimeout(httpClient).(*http.Client) + + assert.Equal(t, 12*time.Minute, result.Timeout) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message.go new file mode 100644 index 0000000..02e2116 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message.go @@ -0,0 +1,233 @@ +package requests + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/consts" +) + +// MergeHeaders merges two or more header prototypes together from left to right +func MergeHeaders(headers ...map[string][]string) map[string][]string { + if len(headers) == 0 { + return make(map[string][]string) + } + out := headers[0] + for _, element := range headers[1:] { + for k, v := range element { + out[k] = v + } + } + return out +} + +// JSONHeaders is a header prototype for JSON based requests +func JSONHeaders() map[string][]string { + return map[string][]string{ + "Content-Type": {"application/json"}, + "Accept": {"application/json"}, + } +} + +// AuthHeader is a header prototype including the App/SDK ID +func AuthHeader(clientSdkId string) map[string][]string { + return map[string][]string{ + "X-Yoti-Auth-Id": {clientSdkId}, + } +} + +// AuthKeyHeader is a header prototype including an encoded RSA PublicKey +func AuthKeyHeader(key *rsa.PublicKey) map[string][]string { + return map[string][]string{ + "X-Yoti-Auth-Key": { + base64.StdEncoding.EncodeToString( + func(a []byte, _ error) []byte { + return a + }(x509.MarshalPKIXPublicKey(key)), + ), + }, + } +} + +// SignedRequest is a builder for constructing a http.Request with Yoti signing +type SignedRequest struct { + Key *rsa.PrivateKey + HTTPMethod string + BaseURL string + Endpoint string + Headers map[string][]string + Params map[string]string + Body []byte + Error error +} + +func (msg *SignedRequest) signDigest(digest []byte) (string, error) { + hash := sha256.Sum256(digest) + signed, err := rsa.SignPKCS1v15(rand.Reader, msg.Key, crypto.SHA256, hash[:]) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(signed), nil +} + +func getTimestamp() string { + return strconv.FormatInt(time.Now().Unix()*1000, 10) +} + +func getNonce() (string, error) { + nonce := make([]byte, 16) + _, err := rand.Read(nonce) + return fmt.Sprintf("%X-%X-%X-%X-%X", nonce[0:4], nonce[4:6], nonce[6:8], nonce[8:10], nonce[10:]), err +} + +// WithPemFile loads the private key from a PEM file reader +func (msg SignedRequest) WithPemFile(in []byte) SignedRequest { + block, _ := pem.Decode(in) + if block == nil { + msg.Error = errors.New("input is not PEM-encoded") + return msg + } + if block.Type != "RSA PRIVATE KEY" { + msg.Error = errors.New("input is not an RSA Private Key") + return msg + } + + msg.Key, msg.Error = x509.ParsePKCS1PrivateKey(block.Bytes) + return msg +} + +func (msg *SignedRequest) addParametersToEndpoint() (string, error) { + if msg.Params == nil { + msg.Params = make(map[string]string) + } + // Add Timestamp/Nonce + if _, ok := msg.Params["nonce"]; !ok { + nonce, err := getNonce() + if err != nil { + return "", err + } + msg.Params["nonce"] = nonce + } + if _, ok := msg.Params["timestamp"]; !ok { + msg.Params["timestamp"] = getTimestamp() + } + + endpoint := msg.Endpoint + if !strings.Contains(endpoint, "?") { + endpoint = endpoint + "?" + } else { + endpoint = endpoint + "&" + } + + var firstParam = true + for param, value := range msg.Params { + var formatString = "%s&%s=%s" + if firstParam { + formatString = "%s%s=%s" + } + endpoint = fmt.Sprintf(formatString, endpoint, param, value) + firstParam = false + } + + return endpoint, nil +} + +func (msg *SignedRequest) generateDigest(endpoint string) (digest string) { + // Generate the message digest + if msg.Body != nil { + digest = fmt.Sprintf( + "%s&%s&%s", + msg.HTTPMethod, + endpoint, + base64.StdEncoding.EncodeToString(msg.Body), + ) + } else { + digest = fmt.Sprintf("%s&%s", + msg.HTTPMethod, + endpoint, + ) + } + return +} + +func (msg *SignedRequest) checkMandatories() error { + if msg.Error != nil { + return msg.Error + } + if msg.Key == nil { + return fmt.Errorf("missing private key") + } + if msg.HTTPMethod == "" { + return fmt.Errorf("missing HTTPMethod") + } + if msg.BaseURL == "" { + return fmt.Errorf("missing BaseURL") + } + if msg.Endpoint == "" { + return fmt.Errorf("missing Endpoint") + } + return nil +} + +// Request builds a http.Request with signature headers +func (msg SignedRequest) Request() (request *http.Request, err error) { + err = msg.checkMandatories() + if err != nil { + return + } + + endpoint, err := msg.addParametersToEndpoint() + if err != nil { + return + } + + signedDigest, err := msg.signDigest([]byte(msg.generateDigest(endpoint))) + if err != nil { + return + } + + // Construct the HTTP Request + request, err = http.NewRequest( + msg.HTTPMethod, + msg.BaseURL+endpoint, + bytes.NewReader(msg.Body), + ) + if err != nil { + return + } + + request.Header.Add("X-Yoti-Auth-Digest", signedDigest) + request.Header.Add("X-Yoti-SDK", consts.SDKIdentifier) + request.Header.Add("X-Yoti-SDK-Version", consts.SDKVersionIdentifier) + + for key, values := range msg.Headers { + for _, value := range values { + request.Header.Add(key, value) + } + } + + return request, err +} + +func Base64ToBase64URL(base64Str string) string { + decoded, err := base64.StdEncoding.DecodeString(base64Str) + if err != nil { + return "" + } + + base64URL := base64.URLEncoding.EncodeToString(decoded) + + return base64URL +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message_test.go new file mode 100644 index 0000000..1e23205 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/requests/signed_message_test.go @@ -0,0 +1,169 @@ +package requests + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "fmt" + "math/rand" + "net/http" + "regexp" + "testing" + + "gotest.tools/v3/assert" +) + +const exampleKey = "MIICXgIBAAKBgQCpTiICtL+ujx8D0FquVWIaXg+ajJadN5hsTlGUXymiFAunSZjLjTsoGfSPz8PJm6pG9ax1Qb+R5UsSgTRTcpZTps2RLRWr5oPfD66bz4l38QXPSvfg5o+5kNxyCb8QANitF7Ht/DcpsGpL7anruHg/RgCLCBFRaGAodfuJCCM9zwIDAQABAoGBAIJL7GbSvjZUVVU1E6TZd0+9lhqmGf/S2o5309bxSfQ/oxxSyrHU9nMNTqcjCZXuJCTKS7hOKmXY5mbOYvvZ0xA7DXfOc+A4LGXQl0r3ZMzhHZTPKboUSh16E4WI4pr98KagFdkeB/0KBURM3x5d/6dSKip8ZpEyqVpuc9d1xtvhAkEAxabfsqfb4fgBsrhZ/qt133yB0FBHs1alRxvUXZWbVPTOegKi5KBdPptf2QfCy8WK3An/lg8cFQG78PyNll/P0QJBANtJBUHTuRDCoYLhqZLdSTQ52qOWRNutZ2fho9ZcLquokB4SFFeC2I4T+s3oSJ8SNh9vW1nNeXW6Zipx+zz8O58CQQCjV9qNGf40zDITEhmFxwt967aYgpAO3O9wScaCpM4fMsWkvaMDEKiewec/RBOvNY0hdb3ctJX/olRAv2b/vCTRAkAuLmCnDlnJR9QP5kp6HZRPJWgAT6NMyGYgoIqKmHtTt3oyewhBrdLBiT+moaa5qXIwiJkqfnV377uYcMzCeTRtAkEAwHdhM3v01GprmHqE2kvlKOXNq9CB1Z4j/vXSQxBYoSrFWLv5nW9e69ngX+n7qhvO3Gs9CBoy/oqOLatFZOuFEw==" + +var keyBytes, _ = base64.StdEncoding.DecodeString(exampleKey) +var privateKey, _ = x509.ParsePKCS1PrivateKey(keyBytes) + +func ExampleMergeHeaders() { + left := map[string][]string{"A": {"Value Of A"}} + right := map[string][]string{"B": {"Value Of B"}} + + merged := MergeHeaders(left, right) + fmt.Println(merged["A"]) + fmt.Println(merged["B"]) + // Output: + // [Value Of A] + // [Value Of B] +} + +func TestMergeHeaders_HandleNullCaseGracefully(t *testing.T) { + assert.Equal(t, len(MergeHeaders()), 0) +} + +func ExampleJSONHeaders() { + jsonHeaders, err := json.Marshal(JSONHeaders()) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(jsonHeaders)) + // Output: {"Accept":["application/json"],"Content-Type":["application/json"]} +} + +func ExampleAuthKeyHeader() { + headers, err := json.Marshal(AuthKeyHeader(&privateKey.PublicKey)) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(headers)) + // Output: {"X-Yoti-Auth-Key":["MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpTiICtL+ujx8D0FquVWIaXg+ajJadN5hsTlGUXymiFAunSZjLjTsoGfSPz8PJm6pG9ax1Qb+R5UsSgTRTcpZTps2RLRWr5oPfD66bz4l38QXPSvfg5o+5kNxyCb8QANitF7Ht/DcpsGpL7anruHg/RgCLCBFRaGAodfuJCCM9zwIDAQAB"]} +} + +func TestRequestShouldBuildForValid(t *testing.T) { + random := rand.New(rand.NewSource(25)) + key, err := rsa.GenerateKey(random, 1024) + + assert.NilError(t, err) + httpMethod := "GET" + baseURL := "example.com" + endpoint := "/" + + request := SignedRequest{ + Key: key, + HTTPMethod: httpMethod, + BaseURL: baseURL, + Endpoint: endpoint, + } + signed, err := request.Request() + assert.NilError(t, err) + assert.Equal(t, httpMethod, signed.Method) + urlCheck, err := regexp.Match(baseURL+endpoint, []byte(signed.URL.String())) + assert.NilError(t, err) + assert.Check(t, urlCheck) + assert.Check(t, signed.Header.Get("X-Yoti-Auth-Digest") != "") + assert.Equal(t, signed.Header.Get("X-Yoti-SDK"), "Go") + assert.Equal(t, signed.Header.Get("X-Yoti-SDK-Version"), "3.14.0") +} + +func TestRequestShouldAddHeaders(t *testing.T) { + random := rand.New(rand.NewSource(25)) + key, err := rsa.GenerateKey(random, 1024) + + assert.NilError(t, err) + httpMethod := "GET" + baseURL := "example.com" + endpoint := "/" + + request := SignedRequest{ + Key: key, + HTTPMethod: httpMethod, + BaseURL: baseURL, + Endpoint: endpoint, + Headers: JSONHeaders(), + } + signed, err := request.Request() + assert.NilError(t, err) + assert.Check(t, signed.Header["X-Yoti-Auth-Digest"][0] != "") + assert.Equal(t, signed.Header["Accept"][0], "application/json") +} + +func TestSignedRequest_checkMandatories_WhenErrorIsSetReturnIt(t *testing.T) { + msg := &SignedRequest{Error: fmt.Errorf("exampleError")} + assert.Error(t, msg.checkMandatories(), "exampleError") +} + +func TestSignedRequest_checkMandatories_WhenKeyMissing(t *testing.T) { + msg := &SignedRequest{} + assert.Error(t, msg.checkMandatories(), "missing private key") +} + +func TestSignedRequest_checkMandatories_WhenHTTPMethodMissing(t *testing.T) { + msg := &SignedRequest{Key: privateKey} + assert.Error(t, msg.checkMandatories(), "missing HTTPMethod") +} + +func TestSignedRequest_checkMandatories_WhenBaseURLMissing(t *testing.T) { + msg := &SignedRequest{ + Key: privateKey, + HTTPMethod: http.MethodPost, + } + assert.Error(t, msg.checkMandatories(), "missing BaseURL") +} + +func TestSignedRequest_checkMandatories_WhenEndpointMissing(t *testing.T) { + msg := &SignedRequest{ + Key: privateKey, + HTTPMethod: http.MethodPost, + BaseURL: "example.com", + } + assert.Error(t, msg.checkMandatories(), "missing Endpoint") +} + +func ExampleSignedRequest_generateDigest() { + msg := &SignedRequest{ + HTTPMethod: http.MethodPost, + Body: []byte("simple message body"), + } + fmt.Println(msg.generateDigest("endpoint")) + // Output: POST&endpoint&c2ltcGxlIG1lc3NhZ2UgYm9keQ== + +} + +func ExampleSignedRequest_WithPemFile() { + msg := SignedRequest{}.WithPemFile([]byte(` +-----BEGIN RSA PRIVATE KEY----- +` + exampleKey + ` +-----END RSA PRIVATE KEY-----`)) + fmt.Println(AuthKeyHeader(&msg.Key.PublicKey)) + // Output: map[X-Yoti-Auth-Key:[MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpTiICtL+ujx8D0FquVWIaXg+ajJadN5hsTlGUXymiFAunSZjLjTsoGfSPz8PJm6pG9ax1Qb+R5UsSgTRTcpZTps2RLRWr5oPfD66bz4l38QXPSvfg5o+5kNxyCb8QANitF7Ht/DcpsGpL7anruHg/RgCLCBFRaGAodfuJCCM9zwIDAQAB]] +} + +func TestSignedRequest_WithPemFile_NotPemEncodedShouldError(t *testing.T) { + msg := SignedRequest{}.WithPemFile([]byte("not pem encoded")) + assert.ErrorContains(t, msg.Error, "not PEM-encoded") +} + +func TestSignedRequest_WithPemFile_NotRSAKeyShouldError(t *testing.T) { + msg := SignedRequest{}.WithPemFile([]byte(`-----BEGIN RSA PUBLIC KEY----- +` + exampleKey + ` +-----END RSA PUBLIC KEY-----`)) + assert.ErrorContains(t, msg.Error, "not an RSA Private Key") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service.go new file mode 100644 index 0000000..8a536ed --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service.go @@ -0,0 +1,316 @@ +package digitalidentity + +import ( + "crypto/rsa" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/digitalidentity/requests" + "github.com/getyoti/yoti-go-sdk/v3/extra" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" +) + +const identitySessionCreationEndpoint = "/v2/sessions" +const identitySessionRetrieval = "/v2/sessions/%s" +const identitySessionQrCodeCreation = "/v2/sessions/%s/qr-codes" +const identitySessionQrCodeRetrieval = "/v2/qr-codes/%s" +const identitySessionReceiptRetrieval = "/v2/receipts/%s" +const identitySessionReceiptKeyRetrieval = "/v2/wrapped-item-keys/%s" +const errorFailedToGetSignedRequest = "failed to get signed request: %v" +const errorFailedToExecuteRequest = "failed to execute request: %v" +const errorFailedToReadBody = "failed to read response body: %v" + +// CreateShareSession creates session using the supplied session specification +func CreateShareSession(httpClient requests.HttpClient, shareSessionRequest *ShareSessionRequest, clientSdkId, apiUrl string, key *rsa.PrivateKey) (*ShareSession, error) { + endpoint := identitySessionCreationEndpoint + + payload, err := shareSessionRequest.MarshalJSON() + if err != nil { + return nil, err + } + + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodPost, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: requests.AuthHeader(clientSdkId), + Body: payload, + Params: map[string]string{"sdkID": clientSdkId}, + }.Request() + if err != nil { + return nil, fmt.Errorf(errorFailedToGetSignedRequest, err) + } + + response, err := requests.Execute(httpClient, request) + if err != nil { + return nil, fmt.Errorf(errorFailedToExecuteRequest, err) + } + + defer response.Body.Close() + shareSession := &ShareSession{} + + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf(errorFailedToReadBody, err) + } + err = json.Unmarshal(responseBytes, shareSession) + return shareSession, err +} + +// GetShareSession get session info using the supplied sessionID parameter +func GetShareSession(httpClient requests.HttpClient, sessionID string, clientSdkId, apiUrl string, key *rsa.PrivateKey) (*ShareSession, error) { + endpoint := fmt.Sprintf(identitySessionRetrieval, sessionID) + + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodGet, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: requests.AuthHeader(clientSdkId), + Params: map[string]string{"sdkID": clientSdkId}, + }.Request() + if err != nil { + return nil, fmt.Errorf(errorFailedToGetSignedRequest, err) + } + + response, err := requests.Execute(httpClient, request) + + if err != nil { + return nil, fmt.Errorf(errorFailedToExecuteRequest, err) + } + defer response.Body.Close() + shareSession := &ShareSession{} + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf(errorFailedToReadBody, err) + } + err = json.Unmarshal(responseBytes, shareSession) + return shareSession, err +} + +// CreateShareQrCode generates a sharing qr code using the supplied sessionID parameter +func CreateShareQrCode(httpClient requests.HttpClient, sessionID string, clientSdkId, apiUrl string, key *rsa.PrivateKey) (*QrCode, error) { + endpoint := fmt.Sprintf(identitySessionQrCodeCreation, sessionID) + + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodPost, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: requests.AuthHeader(clientSdkId), + Body: nil, + Params: map[string]string{"sdkID": clientSdkId}, + }.Request() + if err != nil { + return nil, fmt.Errorf(errorFailedToGetSignedRequest, err) + } + + response, err := requests.Execute(httpClient, request) + if err != nil { + return nil, fmt.Errorf(errorFailedToExecuteRequest, err) + } + + defer response.Body.Close() + qrCode := &QrCode{} + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return nil, fmt.Errorf(errorFailedToReadBody, err) + } + err = json.Unmarshal(responseBytes, qrCode) + return qrCode, err +} + +// GetShareSessionQrCode is used to fetch the qr code by id. +func GetShareSessionQrCode(httpClient requests.HttpClient, qrCodeId string, clientSdkId, apiUrl string, key *rsa.PrivateKey) (fetchedQrCode ShareSessionQrCode, err error) { + endpoint := fmt.Sprintf(identitySessionQrCodeRetrieval, qrCodeId) + headers := requests.AuthHeader(clientSdkId) + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodGet, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: headers, + }.Request() + if err != nil { + return fetchedQrCode, fmt.Errorf(errorFailedToGetSignedRequest, err) + } + + response, err := requests.Execute(httpClient, request) + if err != nil { + return fetchedQrCode, fmt.Errorf(errorFailedToExecuteRequest, err) + } + defer response.Body.Close() + + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return fetchedQrCode, fmt.Errorf(errorFailedToReadBody, err) + } + + err = json.Unmarshal(responseBytes, &fetchedQrCode) + + return fetchedQrCode, err +} + +// GetReceipt fetches receipt info using a receipt id. +func getReceipt(httpClient requests.HttpClient, receiptId string, clientSdkId, apiUrl string, key *rsa.PrivateKey) (receipt ReceiptResponse, err error) { + receiptUrl := requests.Base64ToBase64URL(receiptId) + endpoint := fmt.Sprintf(identitySessionReceiptRetrieval, receiptUrl) + + headers := requests.AuthHeader(clientSdkId) + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodGet, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: headers, + }.Request() + if err != nil { + return receipt, fmt.Errorf(errorFailedToGetSignedRequest, err) + } + + response, err := requests.Execute(httpClient, request) + if err != nil { + return receipt, fmt.Errorf(errorFailedToExecuteRequest, err) + } + defer response.Body.Close() + + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return receipt, fmt.Errorf(errorFailedToReadBody, err) + } + + err = json.Unmarshal(responseBytes, &receipt) + + return receipt, err +} + +// GetReceiptItemKey retrieves the receipt item key for a receipt item key id. +func getReceiptItemKey(httpClient requests.HttpClient, receiptItemKeyId string, clientSdkId, apiUrl string, key *rsa.PrivateKey) (receiptItemKey ReceiptItemKeyResponse, err error) { + endpoint := fmt.Sprintf(identitySessionReceiptKeyRetrieval, receiptItemKeyId) + headers := requests.AuthHeader(clientSdkId) + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodGet, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: headers, + }.Request() + if err != nil { + return receiptItemKey, fmt.Errorf(errorFailedToGetSignedRequest, err) + } + + response, err := requests.Execute(httpClient, request) + if err != nil { + return receiptItemKey, err + } + defer response.Body.Close() + + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return receiptItemKey, err + } + + err = json.Unmarshal(responseBytes, &receiptItemKey) + + return receiptItemKey, err +} + +func GetShareReceipt(httpClient requests.HttpClient, receiptId string, clientSdkId, apiUrl string, key *rsa.PrivateKey) (receipt SharedReceiptResponse, err error) { + receiptResponse, err := getReceipt(httpClient, receiptId, clientSdkId, apiUrl, key) + if err != nil { + return receipt, fmt.Errorf("failed to get receipt: %v", err) + } + + if receiptResponse.Error != "" { + return SharedReceiptResponse{ + ID: receiptResponse.ID, + SessionID: receiptResponse.SessionID, + Timestamp: receiptResponse.Timestamp, + Error: receiptResponse.Error, + ErrorReason: receiptResponse.ErrorReason, + }, nil + } + + itemKeyId := receiptResponse.WrappedItemKeyId + + encryptedItemKeyResponse, err := getReceiptItemKey(httpClient, itemKeyId, clientSdkId, apiUrl, key) + if err != nil { + return receipt, fmt.Errorf("failed to get receipt item key: %v", err) + } + + receiptContentKey, err := cryptoutil.UnwrapReceiptKey(receiptResponse.WrappedKey, encryptedItemKeyResponse.Value, encryptedItemKeyResponse.Iv, key) + if err != nil { + return receipt, fmt.Errorf("failed to unwrap receipt content key: %v", err) + } + + attrData, aextra, err := decryptReceiptContent(receiptResponse.Content, receiptContentKey) + if err != nil { + return receipt, fmt.Errorf("failed to decrypt receipt content: %v", err) + } + + applicationProfile := newApplicationProfile(attrData) + extraDataValue, err := extra.NewExtraData(aextra) + if err != nil { + return receipt, fmt.Errorf("failed to build application extra data: %v", err) + } + + uattrData, uextra, err := decryptReceiptContent(receiptResponse.OtherPartyContent, receiptContentKey) + if err != nil { + return receipt, fmt.Errorf("failed to decrypt receipt other party content: %v", err) + } + + userProfile := newUserProfile(uattrData) + userExtraDataValue, err := extra.NewExtraData(uextra) + if err != nil { + return receipt, fmt.Errorf("failed to build other party extra data: %v", err) + } + + return SharedReceiptResponse{ + ID: receiptResponse.ID, + SessionID: receiptResponse.SessionID, + RememberMeID: receiptResponse.RememberMeID, + ParentRememberMeID: receiptResponse.ParentRememberMeID, + Timestamp: receiptResponse.Timestamp, + UserContent: UserContent{ + UserProfile: userProfile, + ExtraData: userExtraDataValue, + }, + ApplicationContent: ApplicationContent{ + ApplicationProfile: applicationProfile, + ExtraData: extraDataValue, + }, + Error: receiptResponse.Error, + }, nil +} + +func decryptReceiptContent(content *Content, key []byte) (attrData *yotiprotoattr.AttributeList, aextra []byte, err error) { + + if content != nil { + if len(content.Profile) > 0 { + aattr, err := cryptoutil.DecryptReceiptContent(content.Profile, key) + if err != nil { + return nil, nil, fmt.Errorf("failed to decrypt content profile: %v", err) + } + + attrData = &yotiprotoattr.AttributeList{} + if err := proto.Unmarshal(aattr, attrData); err != nil { + return nil, nil, fmt.Errorf("failed to unmarshal attribute list: %v", err) + } + } + + if len(content.ExtraData) > 0 { + aextra, err = cryptoutil.DecryptReceiptContent(content.ExtraData, key) + if err != nil { + return nil, nil, fmt.Errorf("failed to decrypt receipt content extra data: %v", err) + } + } + + } + + return attrData, aextra, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service_test.go new file mode 100644 index 0000000..a36df0d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/service_test.go @@ -0,0 +1,224 @@ +package digitalidentity + +import ( + "crypto/rsa" + "errors" + "fmt" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "io" + "net/http" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "gotest.tools/v3/assert" +) + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func ExampleCreateShareSession() { + key := test.GetValidKey("../test/test-key.pem") + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"id":"0","status":"success","expiry": ""}`)), + }, nil + }, + } + + policy, err := (&PolicyBuilder{}).WithFullName().WithWantedRememberMe().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + session, err := (&ShareSessionRequestBuilder{}).WithPolicy(policy).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + result, err := CreateShareSession(client, &session, "sdkId", "https://apiurl", key) + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Printf("Status code: %s", result.Status) + // Output: Status code: success +} + +func TestCreateShareURL_Unsuccessful_401(t *testing.T) { + _, err := createShareSessionWithErrorResponse(401, `{"id":"8f6a9dfe72128de20909af0d476769b6","status":401,"error":"INVALID_REQUEST_SIGNATURE","message":"Invalid request signature"}`) + + assert.ErrorContains(t, err, "INVALID_REQUEST_SIGNATURE") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func createShareSessionWithErrorResponse(statusCode int, responseBody string) (*ShareSession, error) { + key := test.GetValidKey("../test/test-key.pem") + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: statusCode, + Body: io.NopCloser(strings.NewReader(responseBody)), + }, nil + }, + } + + policy, err := (&PolicyBuilder{}).WithFullName().WithWantedRememberMe().Build() + if err != nil { + return nil, err + } + session, err := (&ShareSessionRequestBuilder{}).WithPolicy(policy).Build() + if err != nil { + return nil, err + } + + return CreateShareSession(client, &session, "sdkId", "https://apiurl", key) +} + +func TestGetShareSession(t *testing.T) { + key := test.GetValidKey("../test/test-key.pem") + mockSessionID := "SOME_SESSION_ID" + mockClientSdkId := "SOME_CLIENT_SDK_ID" + mockApiUrl := "https://example.com/api" + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"id":"SOME_ID","status":"SOME_STATUS","expiry":"SOME_EXPIRY","created":"SOME_CREATED","updated":"SOME_UPDATED","qrCode":{"id":"SOME_QRCODE_ID"},"receipt":{"id":"SOME_RECEIPT_ID"}}`)), + }, nil + }, + } + + _, err := GetShareSession(client, mockSessionID, mockClientSdkId, mockApiUrl, key) + assert.NilError(t, err) + +} + +func TestCreateShareQrCode(t *testing.T) { + key := test.GetValidKey("../test/test-key.pem") + mockSessionID := "SOME_SESSION_ID" + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{}`)), + }, nil + }, + } + + _, err := CreateShareQrCode(client, mockSessionID, "sdkId", "https://apiurl", key) + assert.NilError(t, err) +} + +func TestGetQrCode(t *testing.T) { + key := test.GetValidKey("../test/test-key.pem") + mockQrId := "SOME_QR_CODE_ID" + mockClientSdkId := "SOME_CLIENT_SDK_ID" + mockApiUrl := "https://example.com/api" + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{}`)), + }, nil + }, + } + + _, err := GetShareSessionQrCode(client, mockQrId, mockClientSdkId, mockApiUrl, key) + assert.NilError(t, err) + +} + +// stub cryptoutil.UnwrapReceiptKey to control behaviour in tests +var unwrapReceiptKeyStub = func(wrappedKey, encryptedValue, iv []byte, key *rsa.PrivateKey) ([]byte, error) { + return []byte("receiptContentKey"), nil +} + +// stub decryptReceiptContent to simulate decryption +var decryptReceiptContentStub = func(content *Content, key []byte) (*yotiprotoattr.AttributeList, []byte, error) { + // Return dummy attribute list and extra data bytes + attrList := &yotiprotoattr.AttributeList{ + Attributes: []*yotiprotoattr.Attribute{{Name: "dummy", Value: []byte("value")}}, + } + return attrList, []byte("extraData"), nil +} + +func TestGetShareReceipt_GetReceiptError(t *testing.T) { + key := test.GetValidKey("../test/test-key.pem") + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return nil, errors.New("network error") + }, + } + + _, err := GetShareReceipt(client, "receiptId", "sdkId", "https://apiurl", key) + assert.ErrorContains(t, err, "failed to get receipt") +} + +func TestGetShareReceipt_GetReceiptItemKeyError(t *testing.T) { + key := test.GetValidKey("../test/test-key.pem") + + callCount := 0 + client := &mockHTTPClient{ + do: func(req *http.Request) (*http.Response, error) { + callCount++ + if callCount == 1 { + // return valid receipt JSON with WrappedItemKeyId + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{ + "WrappedItemKeyId": "itemKeyId", + "WrappedKey": "wrappedKeyData" + }`)), + }, nil + } + // simulate error on receipt item key request + return nil, errors.New("item key request error") + }, + } + + _, err := GetShareReceipt(client, "receiptId", "sdkId", "https://apiurl", key) + assert.ErrorContains(t, err, "failed to get receipt") +} + +func TestGetFailureReceipt(t *testing.T) { + key := test.GetValidKey("../test/test-key.pem") + mockQrId := "SOME_QR_CODE_ID" + mockClientSdkId := "SOME_CLIENT_SDK_ID" + mockApiUrl := "https://example.com/api" + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"id":"tXTQK9E22lyzyIhVZM7pY1ctI7FHelLgvrVO35RO+vWKjJJJSJhu2ZLFBCce14Xy","sessionId":"ss.v2.ChZvRm1QTG5tT1FJMjZMYm9xeC1Fdlh3EgdsZDUuZ2Jy","timestamp":"2025-05-21T11:01:07Z","error":"MANDATORY_DOCUMENT_NOT_PROVIDED","errorReason":{"requirements_not_met_details":[{"details":"NOT_APPLICABLE_FOR_SCHEME","audit_id":"97001564-a18a-4afd-bf19-3ffacc88abbb","failure_type":"ID_DOCUMENT_COUNTRY","document_type":"PASSPORT","document_country_iso_code":"IRL"}]}}`)), + }, nil + }, + } + + r, err := GetShareReceipt(client, mockQrId, mockClientSdkId, mockApiUrl, key) + assert.Equal(t, len(r.ErrorReason.RequirementsNotMetDetails), 1) + assert.NilError(t, err) + +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_receipt.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_receipt.go new file mode 100644 index 0000000..399b6dc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_receipt.go @@ -0,0 +1,25 @@ +package digitalidentity + +import "github.com/getyoti/yoti-go-sdk/v3/extra" + +type SharedReceiptResponse struct { + ID string + SessionID string + RememberMeID string + ParentRememberMeID string + Timestamp string + Error string + ErrorReason ErrorReason + UserContent UserContent + ApplicationContent ApplicationContent +} + +type ApplicationContent struct { + ApplicationProfile ApplicationProfile + ExtraData *extra.Data +} + +type UserContent struct { + UserProfile UserProfile + ExtraData *extra.Data +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_retrieve_qr.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_retrieve_qr.go new file mode 100644 index 0000000..fe737d9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_retrieve_qr.go @@ -0,0 +1,10 @@ +package digitalidentity + +type ShareSessionFetchedQrCode struct { + ID string `json:"id"` + Expiry string `json:"expiry"` + Policy string `json:"policy"` + Extensions []interface{} `json:"extensions"` + Session ShareSessionCreated `json:"session"` + RedirectURI string `json:"redirectUri"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session.go new file mode 100644 index 0000000..e27b99f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session.go @@ -0,0 +1,21 @@ +package digitalidentity + +// ShareSession contains information about the session. +type ShareSession struct { + Id string `json:"id"` + Status string `json:"status"` + Expiry string `json:"expiry"` + Created string `json:"created"` + Updated string `json:"updated"` + QrCode qrCode `json:"qrCode"` + Receipt *receipt `json:"receipt"` +} + +type qrCode struct { + Id string `json:"id"` +} + +// receipt containing the receipt id as a string. +type receipt struct { + Id string `json:"id"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder.go new file mode 100644 index 0000000..7f644a2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder.go @@ -0,0 +1,75 @@ +package digitalidentity + +import ( + "encoding/json" +) + +// ShareSessionRequestBuilder builds a session +type ShareSessionRequestBuilder struct { + shareSessionRequest ShareSessionRequest + err error +} + +// ShareSessionRequest represents a sharesession +type ShareSessionRequest struct { + policy Policy + extensions []interface{} + subject *json.RawMessage + shareSessionNotification *ShareSessionNotification + redirectUri string +} + +// WithPolicy attaches a Policy to the ShareSession +func (builder *ShareSessionRequestBuilder) WithPolicy(policy Policy) *ShareSessionRequestBuilder { + builder.shareSessionRequest.policy = policy + return builder +} + +// WithExtension adds an extension to the ShareSession +func (builder *ShareSessionRequestBuilder) WithExtension(extension interface{}) *ShareSessionRequestBuilder { + builder.shareSessionRequest.extensions = append(builder.shareSessionRequest.extensions, extension) + return builder +} + +// WithNotification sets the callback URL +func (builder *ShareSessionRequestBuilder) WithNotification(notification *ShareSessionNotification) *ShareSessionRequestBuilder { + builder.shareSessionRequest.shareSessionNotification = notification + return builder +} + +// WithRedirectUri sets redirectUri to the ShareSession +func (builder *ShareSessionRequestBuilder) WithRedirectUri(redirectUri string) *ShareSessionRequestBuilder { + builder.shareSessionRequest.redirectUri = redirectUri + return builder +} + +// WithSubject adds a subject to the ShareSession. Must be valid JSON. +func (builder *ShareSessionRequestBuilder) WithSubject(subject json.RawMessage) *ShareSessionRequestBuilder { + builder.shareSessionRequest.subject = &subject + return builder +} + +// Build constructs the ShareSession +func (builder *ShareSessionRequestBuilder) Build() (ShareSessionRequest, error) { + if builder.shareSessionRequest.extensions == nil { + builder.shareSessionRequest.extensions = make([]interface{}, 0) + } + return builder.shareSessionRequest, builder.err +} + +// MarshalJSON returns the JSON encoding +func (shareSesssion ShareSessionRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Policy Policy `json:"policy"` + Extensions []interface{} `json:"extensions"` + RedirectUri string `json:"redirectUri"` + Subject *json.RawMessage `json:"subject,omitempty"` + Notification *ShareSessionNotification `json:"notification,omitempty"` + }{ + Policy: shareSesssion.policy, + Extensions: shareSesssion.extensions, + RedirectUri: shareSesssion.redirectUri, + Subject: shareSesssion.subject, + Notification: shareSesssion.shareSessionNotification, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder_test.go new file mode 100644 index 0000000..b0101e2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_builder_test.go @@ -0,0 +1,99 @@ +package digitalidentity + +import ( + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/extension" +) + +func ExampleShareSessionRequestBuilder() { + shareSession, err := (&ShareSessionRequestBuilder{}).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := shareSession.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":null,"wanted_auth_types":null,"wanted_remember_me":false},"extensions":[],"redirectUri":""} +} + +func ExampleShareSessionRequestBuilder_WithPolicy() { + policy, err := (&PolicyBuilder{}).WithEmail().WithPinAuth().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + session, err := (&ShareSessionRequestBuilder{}).WithPolicy(policy).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := session.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":[{"name":"email_address","accept_self_asserted":false}],"wanted_auth_types":[2],"wanted_remember_me":false},"extensions":[],"redirectUri":""} +} + +func ExampleShareSessionRequestBuilder_WithExtension() { + policy, err := (&PolicyBuilder{}).WithFullName().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + builtExtension, err := (&extension.TransactionalFlowExtensionBuilder{}). + WithContent("Transactional Flow Extension"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + session, err := (&ShareSessionRequestBuilder{}).WithExtension(builtExtension).WithPolicy(policy).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := session.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":[{"name":"full_name","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false},"extensions":[{"type":"TRANSACTIONAL_FLOW","content":"Transactional Flow Extension"}],"redirectUri":""} +} + +func ExampleShareSessionRequestBuilder_WithSubject() { + subject := []byte(`{ + "subject_id": "some_subject_id_string" + }`) + + session, err := (&ShareSessionRequestBuilder{}).WithSubject(subject).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := session.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":null,"wanted_auth_types":null,"wanted_remember_me":false},"extensions":[],"redirectUri":"","subject":{"subject_id":"some_subject_id_string"}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_created.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_created.go new file mode 100644 index 0000000..86d60e3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_created.go @@ -0,0 +1,8 @@ +package digitalidentity + +// ShareSessionCreated Share Session QR Result +type ShareSessionCreated struct { + ID string `json:"id"` + Satus string `json:"status"` + Expiry string `json:"expiry"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder.go new file mode 100644 index 0000000..6c873eb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder.go @@ -0,0 +1,62 @@ +package digitalidentity + +import ( + "encoding/json" +) + +// ShareSessionNotification specifies the session notification configuration. +type ShareSessionNotification struct { + url string + method *string + verifyTLS *bool + headers map[string][]string +} + +// ShareSessionNotificationBuilder builds Share Session Notification +type ShareSessionNotificationBuilder struct { + shareSessionNotification ShareSessionNotification +} + +// WithUrl setsUrl to Share Session Notification +func (b *ShareSessionNotificationBuilder) WithUrl(url string) *ShareSessionNotificationBuilder { + b.shareSessionNotification.url = url + return b +} + +// WithMethod set method to Share Session Notification +func (b *ShareSessionNotificationBuilder) WithMethod(method string) *ShareSessionNotificationBuilder { + b.shareSessionNotification.method = &method + return b +} + +// WithVerifyTLS sets whether TLS should be verified for notifications. +func (b *ShareSessionNotificationBuilder) WithVerifyTls(verifyTls bool) *ShareSessionNotificationBuilder { + b.shareSessionNotification.verifyTLS = &verifyTls + return b +} + +// WithHeaders set headers to Share Session Notification +func (b *ShareSessionNotificationBuilder) WithHeaders(headers map[string][]string) *ShareSessionNotificationBuilder { + b.shareSessionNotification.headers = headers + return b +} + +// Build constructs the Share Session Notification Builder +func (b *ShareSessionNotificationBuilder) Build() (ShareSessionNotification, error) { + return b.shareSessionNotification, nil +} + +// MarshalJSON returns the JSON encoding +func (a *ShareSessionNotification) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Url string `json:"url"` + Method *string `json:"method,omitempty"` + VerifyTls *bool `json:"verifyTls,omitempty"` + Headers map[string][]string `json:"headers,omitempty"` + }{ + Url: a.url, + Method: a.method, + VerifyTls: a.verifyTLS, + Headers: a.headers, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder_test.go new file mode 100644 index 0000000..a60e301 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_notification_builder_test.go @@ -0,0 +1,95 @@ +package digitalidentity + +import ( + "fmt" +) + +func ExampleShareSessionNotificationBuilder() { + shareSessionNotify, err := (&ShareSessionNotificationBuilder{}).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := shareSessionNotify.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"url":""} +} + +func ExampleShareSessionNotificationBuilder_WithUrl() { + shareSessionNotify, err := (&ShareSessionNotificationBuilder{}).WithUrl("Custom_Url").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := shareSessionNotify.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"url":"Custom_Url"} +} + +func ExampleShareSessionNotificationBuilder_WithMethod() { + shareSessionNotify, err := (&ShareSessionNotificationBuilder{}).WithMethod("CUSTOMMETHOD").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := shareSessionNotify.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"url":"","method":"CUSTOMMETHOD"} +} + +func ExampleShareSessionNotificationBuilder_WithVerifyTls() { + + shareSessionNotify, err := (&ShareSessionNotificationBuilder{}).WithVerifyTls(true).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := shareSessionNotify.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"url":"","verifyTls":true} +} + +func ExampleShareSessionNotificationBuilder_WithHeaders() { + + headers := make(map[string][]string) + headers["key"] = append(headers["key"], "value") + + shareSessionNotify, err := (&ShareSessionNotificationBuilder{}).WithHeaders(headers).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := shareSessionNotify.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"url":"","headers":{"key":["value"]}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_qr_code.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_qr_code.go new file mode 100644 index 0000000..5f98caa --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/share_session_qr_code.go @@ -0,0 +1,14 @@ +package digitalidentity + +type ShareSessionQrCode struct { + ID string `json:"id"` + Expiry string `json:"expiry"` + Policy string `json:"policy"` + Extensions []interface{} `json:"extensions"` + Session struct { + ID string `json:"id"` + Status string `json:"status"` + Expiry string `json:"expiry"` + } `json:"session"` + RedirectURI string `json:"redirectUri"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/source_constraint.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/source_constraint.go new file mode 100644 index 0000000..079007f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/source_constraint.go @@ -0,0 +1,105 @@ +package digitalidentity + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +// Anchor name constants +const ( + AnchorDrivingLicenceConst = "DRIVING_LICENCE" + AnchorPassportConst = "PASSPORT" + AnchorNationalIDConst = "NATIONAL_ID" + AnchorPassCardConst = "PASS_CARD" +) + +// SourceConstraint describes a requirement or preference for a particular set +// of anchors +type SourceConstraint struct { + anchors []WantedAnchor + softPreference bool +} + +// SourceConstraintBuilder builds a source constraint +type SourceConstraintBuilder struct { + sourceConstraint SourceConstraint + err error +} + +// WithAnchorByValue is a helper method which builds an anchor and adds it to +// the source constraint +func (b *SourceConstraintBuilder) WithAnchorByValue(value, subtype string) *SourceConstraintBuilder { + anchor, err := (&WantedAnchorBuilder{}). + WithValue(value). + WithSubType(subtype). + Build() + if err != nil { + b.err = yotierror.MultiError{This: err, Next: b.err} + } + + return b.WithAnchor(anchor) +} + +// WithAnchor adds an anchor to the preference list +func (b *SourceConstraintBuilder) WithAnchor(anchor WantedAnchor) *SourceConstraintBuilder { + b.sourceConstraint.anchors = append(b.sourceConstraint.anchors, anchor) + return b +} + +// WithPassport adds a passport anchor +func (b *SourceConstraintBuilder) WithPassport(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorPassportConst, subtype) +} + +// WithDrivingLicence adds a Driving Licence anchor +func (b *SourceConstraintBuilder) WithDrivingLicence(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorDrivingLicenceConst, subtype) +} + +// WithNationalID adds a national ID anchor +func (b *SourceConstraintBuilder) WithNationalID(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorNationalIDConst, subtype) +} + +// WithPasscard adds a passcard anchor +func (b *SourceConstraintBuilder) WithPasscard(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorPassCardConst, subtype) +} + +// WithSoftPreference sets this constraint as a 'soft requirement' if the +// parameter is true, and a hard requirement if it is false. +func (b *SourceConstraintBuilder) WithSoftPreference(soft bool) *SourceConstraintBuilder { + b.sourceConstraint.softPreference = soft + return b +} + +// Build builds a SourceConstraint +func (b *SourceConstraintBuilder) Build() (SourceConstraint, error) { + if b.sourceConstraint.anchors == nil { + b.sourceConstraint.anchors = make([]WantedAnchor, 0) + } + return b.sourceConstraint, b.err +} + +func (constraint *SourceConstraint) isConstraint() bool { + return true +} + +// MarshalJSON returns the JSON encoding +func (constraint *SourceConstraint) MarshalJSON() ([]byte, error) { + type PreferenceList struct { + Anchors []WantedAnchor `json:"anchors"` + SoftPreference bool `json:"soft_preference"` + } + return json.Marshal(&struct { + Type string `json:"type"` + PreferredSources PreferenceList `json:"preferred_sources"` + }{ + Type: "SOURCE", + PreferredSources: PreferenceList{ + Anchors: constraint.anchors, + SoftPreference: constraint.softPreference, + }, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile.go new file mode 100644 index 0000000..32a4d28 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile.go @@ -0,0 +1,182 @@ +package digitalidentity + +import ( + "strings" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// UserProfile 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 UserProfile struct { + baseProfile +} + +// Creates a new Profile struct +func newUserProfile(attributes *yotiprotoattr.AttributeList) UserProfile { + return UserProfile{ + baseProfile{ + attributeSlice: createAttributeSlice(attributes), + }, + } +} + +func createAttributeSlice(protoAttributeList *yotiprotoattr.AttributeList) (result []*yotiprotoattr.Attribute) { + if protoAttributeList != nil { + result = append(result, protoAttributeList.Attributes...) + } + + return result +} + +// Selfie is a photograph of the user. Will be nil if not provided by Yoti. +func (p UserProfile) Selfie() *attribute.ImageAttribute { + return p.GetImageAttribute(consts.AttrSelfie) +} + +// GetSelfieAttributeByID retrieve a Selfie attribute by ID on the Yoti profile. +// This attribute is a photograph of the user. +// Will return nil if attribute is not present. +func (p UserProfile) GetSelfieAttributeByID(attributeID string) (*attribute.ImageAttribute, error) { + for _, a := range p.attributeSlice { + if a.EphemeralId == attributeID { + return attribute.NewImage(a) + } + } + return nil, nil +} + +// GivenNames corresponds to secondary names in passport, and first/middle names in English. Will be nil if not provided by Yoti. +func (p UserProfile) GivenNames() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrGivenNames) +} + +// FamilyName corresponds to primary name in passport, and surname in English. Will be nil if not provided by Yoti. +func (p UserProfile) FamilyName() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrFamilyName) +} + +// FullName represents the user's full name. +// If family_name/given_names are present, the value will be equal to the string 'given_names + " " family_name'. +// Will be nil if not provided by Yoti. +func (p UserProfile) FullName() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrFullName) +} + +// MobileNumber represents the user's mobile phone number, as verified at registration time. +// The value will be a number in E.164 format (i.e. '+' for international prefix and no spaces, e.g. "+447777123456"). +// Will be nil if not provided by Yoti. +func (p UserProfile) MobileNumber() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrMobileNumber) +} + +// EmailAddress represents the user's verified email address. Will be nil if not provided by Yoti. +func (p UserProfile) EmailAddress() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrEmailAddress) +} + +// 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 UserProfile) DateOfBirth() (*attribute.DateAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == consts.AttrDateOfBirth { + return attribute.NewDate(a) + } + } + return nil, nil +} + +// Address represents the user's address. Will be nil if not provided by Yoti. +func (p UserProfile) Address() *attribute.StringAttribute { + addressAttribute := p.GetStringAttribute(consts.AttrAddress) + if addressAttribute == nil { + return ensureAddressProfile(&p) + } + + return addressAttribute +} + +// StructuredPostalAddress represents the user's address in a JSON format. +// Will be nil if not provided by Yoti. This can be accessed as a +// map[string]string{} using a type assertion, e.g.: +// structuredPostalAddress := structuredPostalAddressAttribute.Value().(map[string]string{}) +func (p UserProfile) StructuredPostalAddress() (*attribute.JSONAttribute, error) { + return p.GetJSONAttribute(consts.AttrStructuredPostalAddress) +} + +// Gender corresponds to the gender in the registered document; the value will be one of the strings "MALE", "FEMALE", "TRANSGENDER" or "OTHER". +// Will be nil if not provided by Yoti. +func (p UserProfile) Gender() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrGender) +} + +// Nationality corresponds to the nationality in the passport. +// The value is an ISO-3166-1 alpha-3 code with ICAO9303 (passport) extensions. +// Will be nil if not provided by Yoti. +func (p UserProfile) Nationality() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrNationality) +} + +// DocumentImages returns a slice of document images cropped from the image in the capture page. +// There can be multiple images as per the number of regions in the capture in this attribute. +// Will be nil if not provided by Yoti. +func (p UserProfile) DocumentImages() (*attribute.ImageSliceAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == consts.AttrDocumentImages { + return attribute.NewImageSlice(a) + } + } + return nil, nil +} + +// GetDocumentImagesAttributeByID retrieve a Document Images attribute by ID on the Yoti profile. +// This attribute consists of a slice of document images cropped from the image in the capture page. +// There can be multiple images as per the number of regions in the capture in this attribute. +// Will return nil if attribute is not present. +func (p UserProfile) GetDocumentImagesAttributeByID(attributeID string) (*attribute.ImageSliceAttribute, error) { + for _, a := range p.attributeSlice { + if a.EphemeralId == attributeID { + return attribute.NewImageSlice(a) + } + } + return nil, nil +} + +// DocumentDetails represents information extracted from a document provided by the user. +// Will be nil if not provided by Yoti. +func (p UserProfile) DocumentDetails() (*attribute.DocumentDetailsAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == consts.AttrDocumentDetails { + return attribute.NewDocumentDetails(a) + } + } + return nil, nil +} + +// IdentityProfileReport represents the JSON object containing identity assertion and the +// verification report. Will be nil if not provided by Yoti. +func (p UserProfile) IdentityProfileReport() (*attribute.JSONAttribute, error) { + return p.GetJSONAttribute(consts.AttrIdentityProfileReport) +} + +// AgeVerifications returns a slice of age verifications for the user. +// Will be an empty slice if not provided by Yoti. +func (p UserProfile) AgeVerifications() (out []attribute.AgeVerification, err error) { + ageUnderString := strings.Replace(consts.AttrAgeUnder, "%d", "", -1) + ageOverString := strings.Replace(consts.AttrAgeOver, "%d", "", -1) + + for _, a := range p.attributeSlice { + if strings.HasPrefix(a.Name, ageUnderString) || + strings.HasPrefix(a.Name, ageOverString) { + verification, err := attribute.NewAgeVerification(a) + if err != nil { + return nil, err + } + out = append(out, verification) + } + } + return out, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile_test.go new file mode 100644 index 0000000..b81b78e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/user_profile_test.go @@ -0,0 +1,704 @@ +package digitalidentity + +import ( + "encoding/base64" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/file" + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +const ( + attributeName = "test_attribute_name" + attributeValueString = "value" + + documentImagesAttributeID = "document-images-attribute-id-123" + selfieAttributeID = "selfie-attribute-id-123" + fullNameAttributeID = "full-name-id-123" +) + +var attributeValue = []byte(attributeValueString) + +func getUserProfile() UserProfile { + userProfile := createProfileWithMultipleAttributes( + createDocumentImagesAttribute(documentImagesAttributeID), + createSelfieAttribute(yotiprotoattr.ContentType_JPEG, selfieAttributeID), + createStringAttribute("full_name", []byte("John Smith"), []*yotiprotoattr.Anchor{}, fullNameAttributeID)) + + return userProfile +} + +func ExampleUserProfile_GetAttributeByID() { + userProfile := getUserProfile() + fullNameAttribute := userProfile.GetAttributeByID("full-name-id-123") + value := fullNameAttribute.Value().(string) + + fmt.Println(value) + // Output: John Smith +} + +func ExampleUserProfile_GetDocumentImagesAttributeByID() { + userProfile := getUserProfile() + documentImagesAttribute, err := userProfile.GetDocumentImagesAttributeByID("document-images-attribute-id-123") + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(*documentImagesAttribute.ID()) + // Output: document-images-attribute-id-123 +} + +func ExampleUserProfile_GetSelfieAttributeByID() { + userProfile := getUserProfile() + selfieAttribute, err := userProfile.GetSelfieAttributeByID("selfie-attribute-id-123") + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(*selfieAttribute.ID()) + // Output: selfie-attribute-id-123 +} + +func createProfileWithSingleAttribute(attr *yotiprotoattr.Attribute) UserProfile { + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, attr) + + return UserProfile{ + baseProfile{ + attributeSlice: attributeSlice, + }, + } +} + +func createAppProfileWithSingleAttribute(attr *yotiprotoattr.Attribute) ApplicationProfile { + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, attr) + + return ApplicationProfile{ + baseProfile{ + attributeSlice: attributeSlice, + }, + } +} + +func createProfileWithMultipleAttributes(list ...*yotiprotoattr.Attribute) UserProfile { + return UserProfile{ + baseProfile{ + attributeSlice: list, + }, + } +} + +func TestProfile_AgeVerifications(t *testing.T) { + ageOver14 := &yotiprotoattr.Attribute{ + Name: "age_over:14", + Value: []byte("true"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + ageUnder18 := &yotiprotoattr.Attribute{ + Name: "age_under:18", + Value: []byte("true"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + ageOver18 := &yotiprotoattr.Attribute{ + Name: "age_over:18", + Value: []byte("false"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithMultipleAttributes(ageOver14, ageUnder18, ageOver18) + ageVerifications, err := profile.AgeVerifications() + + assert.NilError(t, err) + assert.Equal(t, len(ageVerifications), 3) + + assert.Equal(t, ageVerifications[0].Age, 14) + assert.Equal(t, ageVerifications[0].CheckType, "age_over") + assert.Equal(t, ageVerifications[0].Result, true) + + assert.Equal(t, ageVerifications[1].Age, 18) + assert.Equal(t, ageVerifications[1].CheckType, "age_under") + assert.Equal(t, ageVerifications[1].Result, true) + + assert.Equal(t, ageVerifications[2].Age, 18) + assert.Equal(t, ageVerifications[2].CheckType, "age_over") + assert.Equal(t, ageVerifications[2].Result, false) +} + +func TestProfile_GetAttribute_EmptyString(t *testing.T) { + emptyString := "" + attributeValue := []byte(emptyString) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, att.Name(), attributeName) + assert.Equal(t, att.Value().(string), emptyString) +} + +func TestProfile_GetApplicationAttribute(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createProfileWithSingleAttribute(attr) + applicationAttribute := appProfile.GetAttribute(attributeName) + assert.Equal(t, applicationAttribute.Name(), attributeName) +} + +func TestProfile_GetApplicationName(t *testing.T) { + attributeValue := "APPLICATION NAME" + var attr = &yotiprotoattr.Attribute{ + Name: "application_name", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, attributeValue, appProfile.ApplicationName().Value()) +} + +func TestProfile_GetApplicationURL(t *testing.T) { + attributeValue := "APPLICATION URL" + var attr = &yotiprotoattr.Attribute{ + Name: "application_url", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, attributeValue, appProfile.ApplicationURL().Value()) +} + +func TestProfile_GetApplicationLogo(t *testing.T) { + attributeValue := "APPLICATION LOGO" + var attr = &yotiprotoattr.Attribute{ + Name: "application_logo", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, 16, len(appProfile.ApplicationLogo().Value().Data())) +} + +func TestProfile_GetApplicationBGColor(t *testing.T) { + attributeValue := "BG VALUE" + var attr = &yotiprotoattr.Attribute{ + Name: "application_receipt_bgcolor", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, attributeValue, appProfile.ApplicationReceiptBgColor().Value()) +} + +func TestProfile_GetAttribute_Int(t *testing.T) { + intValues := [5]int{0, 1, 123, -10, -1} + + for _, integer := range intValues { + assertExpectedIntegerIsReturned(t, integer) + } +} + +func assertExpectedIntegerIsReturned(t *testing.T, intValue int) { + intAsString := strconv.Itoa(intValue) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: []byte(intAsString), + ContentType: yotiprotoattr.ContentType_INT, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, att.Value().(int), intValue) +} + +func TestProfile_GetAttribute_InvalidInt_ReturnsNil(t *testing.T) { + invalidIntValue := "1985-01-01" + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: []byte(invalidIntValue), + ContentType: yotiprotoattr.ContentType_INT, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + + att := result.GetAttribute(attributeName) + + assert.Assert(t, is.Nil(att)) +} + +func TestProfile_EmptyStringIsAllowed(t *testing.T) { + emptyString := "" + attrValue := []byte(emptyString) + + var attr = &yotiprotoattr.Attribute{ + Name: consts.AttrGender, + Value: attrValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(attr) + att := profile.Gender() + + assert.Equal(t, att.Value(), emptyString) +} + +func TestProfile_GetAttribute_Time(t *testing.T) { + dateStringValue := "1985-01-01" + expectedDate := time.Date(1985, time.January, 1, 0, 0, 0, 0, time.UTC) + + attributeValueTime := []byte(dateStringValue) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValueTime, + ContentType: yotiprotoattr.ContentType_DATE, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, expectedDate, att.Value().(*time.Time).UTC()) +} + +func TestProfile_GetAttribute_Jpeg(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(attr) + att := profile.GetAttribute(attributeName) + + expected := media.JPEGImage(attributeValue) + result := att.Value().(media.JPEGImage) + + assert.DeepEqual(t, expected, result) + assert.Equal(t, expected.Base64URL(), result.Base64URL()) +} + +func TestProfile_GetAttribute_Png(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(attr) + att := profile.GetAttribute(attributeName) + + expected := media.PNGImage(attributeValue) + result := att.Value().(media.PNGImage) + + assert.DeepEqual(t, expected, result) + assert.Equal(t, expected.Base64URL(), result.Base64URL()) +} + +func TestProfile_GetAttribute_Bool(t *testing.T) { + var initialBoolValue = true + attrValue := []byte(strconv.FormatBool(initialBoolValue)) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attrValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + boolValue, err := strconv.ParseBool(att.Value().(string)) + + assert.NilError(t, err) + assert.Equal(t, initialBoolValue, boolValue) +} + +func TestProfile_GetAttribute_JSON(t *testing.T) { + addressFormat := "2" + + var structuredAddressBytes = []byte(` + { + "address_format": "` + addressFormat + `", + "building": "House No.86-A" + }`) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: structuredAddressBytes, + ContentType: yotiprotoattr.ContentType_JSON, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + retrievedAttributeMap := att.Value().(map[string]interface{}) + actualAddressFormat := retrievedAttributeMap["address_format"] + + assert.Equal(t, actualAddressFormat, addressFormat) +} + +func TestProfile_GetAttribute_Undefined(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, att.Name(), attributeName) + assert.Equal(t, att.Value().(string), attributeValueString) +} + +func TestProfile_GetAttribute_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result := userProfile.GetAttribute("attributeName") + + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_GetAttributeByID(t *testing.T) { + attributeID := "att-id-123" + + var attr1 = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + EphemeralId: attributeID, + } + var attr2 = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + EphemeralId: "non-matching-attribute-ID", + } + + profile := createProfileWithMultipleAttributes(attr1, attr2) + + result := profile.GetAttributeByID(attributeID) + assert.DeepEqual(t, result.ID(), &attributeID) +} + +func TestProfile_GetAttributeByID_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result := userProfile.GetAttributeByID("attributeName") + + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_GetDocumentImagesAttributeByID_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result, err := userProfile.GetDocumentImagesAttributeByID("attributeName") + assert.NilError(t, err) + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_GetSelfieAttributeByID_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result, err := userProfile.GetSelfieAttributeByID("attributeName") + assert.NilError(t, err) + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_StringAttribute(t *testing.T) { + nationalityName := consts.AttrNationality + + var as = &yotiprotoattr.Attribute{ + Name: nationalityName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(as) + + assert.Equal(t, result.Nationality().Value(), attributeValueString) + + assert.Equal(t, result.Nationality().ContentType(), yotiprotoattr.ContentType_STRING.String()) +} + +func TestProfile_AttributeProperty_RetrievesAttribute(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.Equal(t, selfie.Name(), consts.AttrSelfie) + assert.DeepEqual(t, attributeValue, selfie.Value().Data()) + assert.Equal(t, selfie.ContentType(), yotiprotoattr.ContentType_PNG.String()) +} + +func TestProfile_DocumentDetails_RetrievesAttribute(t *testing.T) { + documentDetailsName := consts.AttrDocumentDetails + attributeValue := []byte("PASSPORT GBR 1234567") + + var protoAttribute = &yotiprotoattr.Attribute{ + Name: documentDetailsName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: make([]*yotiprotoattr.Anchor, 0), + } + + result := createProfileWithSingleAttribute(protoAttribute) + documentDetails, err := result.DocumentDetails() + assert.NilError(t, err) + + assert.Equal(t, documentDetails.Value().DocumentType, "PASSPORT") +} + +func TestProfile_DocumentImages_RetrievesAttribute(t *testing.T) { + protoAttribute := createDocumentImagesAttribute("attr-id") + + result := createProfileWithSingleAttribute(protoAttribute) + documentImages, err := result.DocumentImages() + assert.NilError(t, err) + + assert.Equal(t, documentImages.Name(), consts.AttrDocumentImages) +} + +func TestProfile_AttributesReturnsNilWhenNotPresent(t *testing.T) { + documentImagesName := consts.AttrDocumentImages + multiValue, err := proto.Marshal(&yotiprotoattr.MultiValue{}) + assert.NilError(t, err) + + protoAttribute := &yotiprotoattr.Attribute{ + Name: documentImagesName, + Value: multiValue, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: make([]*yotiprotoattr.Anchor, 0), + } + + result := createProfileWithSingleAttribute(protoAttribute) + + DoB, err := result.DateOfBirth() + assert.Check(t, DoB == nil) + assert.Check(t, err == nil) + assert.Check(t, result.Address() == nil) +} + +func TestMissingPostalAddress_UsesFormattedAddress(t *testing.T) { + var formattedAddressText = `House No.86-A\nRajgura Nagar\nLudhina\nPunjab\n141012\nIndia` + + var structuredAddressBytes = []byte(` + { + "address_format": 2, + "building": "House No.86-A", + "formatted_address": "` + formattedAddressText + `" + } + `) + + var jsonAttribute = &yotiprotoattr.Attribute{ + Name: consts.AttrStructuredPostalAddress, + Value: structuredAddressBytes, + ContentType: yotiprotoattr.ContentType_JSON, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(jsonAttribute) + + ensureAddressProfile(&profile) + + escapedFormattedAddressText := strings.Replace(formattedAddressText, `\n`, "\n", -1) + + profileAddress := profile.Address().Value() + assert.Equal(t, profileAddress, escapedFormattedAddressText, "Address does not equal the expected formatted address.") + + structuredPostalAddress, err := profile.StructuredPostalAddress() + assert.NilError(t, err) + assert.Equal(t, structuredPostalAddress.ContentType(), "JSON") +} + +func TestAttributeImage_Image_Png(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.DeepEqual(t, selfie.Value().Data(), attributeValue) +} + +func TestAttributeImage_Image_Jpeg(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_JPEG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.DeepEqual(t, selfie.Value().Data(), attributeValue) +} + +func TestAttributeImage_Image_Default(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.DeepEqual(t, selfie.Value().Data(), attributeValue) +} +func TestAttributeImage_Base64Selfie_Png(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(attributeValue) + expectedBase64Selfie := "data:image/png;base64," + base64ImageExpectedValue + base64Selfie := result.Selfie().Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} + +func TestAttributeImage_Base64URL_Jpeg(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_JPEG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(attributeValue) + + expectedBase64Selfie := "data:image/jpeg;base64," + base64ImageExpectedValue + + base64Selfie := result.Selfie().Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} + +func TestProfile_IdentityProfileReport_RetrievesAttribute(t *testing.T) { + identityProfileReportJSON, err := file.ReadFile("../test/fixtures/RTWIdentityProfileReport.json") + assert.NilError(t, err) + + var attr = &yotiprotoattr.Attribute{ + Name: consts.AttrIdentityProfileReport, + Value: identityProfileReportJSON, + ContentType: yotiprotoattr.ContentType_JSON, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att, err := result.IdentityProfileReport() + assert.NilError(t, err) + + retrievedIdentityProfile := att.Value() + gotProof := retrievedIdentityProfile["proof"] + + assert.Equal(t, gotProof, "") +} + +func TestProfileAllowsMultipleAttributesWithSameName(t *testing.T) { + firstAttribute := createStringAttribute("full_name", []byte("some_value"), []*yotiprotoattr.Anchor{}, "id") + secondAttribute := createStringAttribute("full_name", []byte("some_other_value"), []*yotiprotoattr.Anchor{}, "id") + + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, firstAttribute, secondAttribute) + + var profile = UserProfile{ + baseProfile{ + attributeSlice: attributeSlice, + }, + } + + var fullNames = profile.GetAttributes("full_name") + + assert.Assert(t, is.Equal(len(fullNames), 2)) + assert.Assert(t, is.Equal(fullNames[0].Value().(string), "some_value")) + assert.Assert(t, is.Equal(fullNames[1].Value().(string), "some_other_value")) +} + +func createStringAttribute(name string, value []byte, anchors []*yotiprotoattr.Anchor, attributeID string) *yotiprotoattr.Attribute { + return &yotiprotoattr.Attribute{ + Name: name, + Value: value, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: anchors, + EphemeralId: attributeID, + } +} + +func createSelfieAttribute(contentType yotiprotoattr.ContentType, attributeID string) *yotiprotoattr.Attribute { + var attributeImage = &yotiprotoattr.Attribute{ + Name: consts.AttrSelfie, + Value: attributeValue, + ContentType: contentType, + Anchors: []*yotiprotoattr.Anchor{}, + EphemeralId: attributeID, + } + return attributeImage +} + +func createDocumentImagesAttribute(attributeID string) *yotiprotoattr.Attribute { + multiValue, err := proto.Marshal(&yotiprotoattr.MultiValue{}) + if err != nil { + panic(err) + } + + protoAttribute := &yotiprotoattr.Attribute{ + Name: consts.AttrDocumentImages, + Value: multiValue, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: make([]*yotiprotoattr.Anchor, 0), + EphemeralId: attributeID, + } + return protoAttribute +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder.go new file mode 100644 index 0000000..855c9c3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder.go @@ -0,0 +1,44 @@ +package digitalidentity + +import ( + "encoding/json" +) + +// WantedAnchor specifies a preferred anchor for a user's details +type WantedAnchor struct { + name string + subType string +} + +// WantedAnchorBuilder describes a desired anchor for user profile data +type WantedAnchorBuilder struct { + wantedAnchor WantedAnchor +} + +// WithValue sets the anchor's name +func (b *WantedAnchorBuilder) WithValue(name string) *WantedAnchorBuilder { + b.wantedAnchor.name = name + return b +} + +// WithSubType sets the anchors subtype +func (b *WantedAnchorBuilder) WithSubType(subType string) *WantedAnchorBuilder { + b.wantedAnchor.subType = subType + return b +} + +// Build constructs the anchor from the builder's specification +func (b *WantedAnchorBuilder) Build() (WantedAnchor, error) { + return b.wantedAnchor, nil +} + +// MarshalJSON ... +func (a *WantedAnchor) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Name string `json:"name"` + SubType string `json:"sub_type"` + }{ + Name: a.name, + SubType: a.subType, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder_test.go new file mode 100644 index 0000000..907d8e0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_anchor_builder_test.go @@ -0,0 +1,25 @@ +package digitalidentity + +import ( + "fmt" +) + +func ExampleWantedAnchorBuilder() { + aadhaarAnchor, err := (&WantedAnchorBuilder{}). + WithValue("NATIONAL_ID"). + WithSubType("AADHAAR"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + aadhaarJSON, err := aadhaarAnchor.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println("Aadhaar:", string(aadhaarJSON)) + // Output: Aadhaar: {"name":"NATIONAL_ID","sub_type":"AADHAAR"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_builder.go new file mode 100644 index 0000000..d004b96 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_builder.go @@ -0,0 +1,81 @@ +package digitalidentity + +import ( + "encoding/json" + "errors" +) + +type constraintInterface interface { + MarshalJSON() ([]byte, error) + isConstraint() bool // This function is not used but makes inheritance explicit +} + +// WantedAttributeBuilder generates the payload for specifying a single wanted +// attribute as part of a dynamic scenario +type WantedAttributeBuilder struct { + attr WantedAttribute +} + +// WantedAttribute represents a wanted attribute in a dynamic sharing policy +type WantedAttribute struct { + name string + derivation string + constraints []constraintInterface + acceptSelfAsserted bool + Optional bool +} + +// WithName sets the name of the wanted attribute +func (builder *WantedAttributeBuilder) WithName(name string) *WantedAttributeBuilder { + builder.attr.name = name + return builder +} + +// WithDerivation sets the derivation +func (builder *WantedAttributeBuilder) WithDerivation(derivation string) *WantedAttributeBuilder { + builder.attr.derivation = derivation + return builder +} + +// WithConstraint adds a constraint to a wanted attribute +func (builder *WantedAttributeBuilder) WithConstraint(constraint constraintInterface) *WantedAttributeBuilder { + builder.attr.constraints = append(builder.attr.constraints, constraint) + return builder +} + +// WithAcceptSelfAsserted allows self-asserted user details, such as those from Aadhar +func (builder *WantedAttributeBuilder) WithAcceptSelfAsserted(accept bool) *WantedAttributeBuilder { + builder.attr.acceptSelfAsserted = accept + return builder +} + +// Build generates the wanted attribute's specification +func (builder *WantedAttributeBuilder) Build() (WantedAttribute, error) { + if builder.attr.constraints == nil { + builder.attr.constraints = make([]constraintInterface, 0) + } + + var err error + if len(builder.attr.name) == 0 { + err = errors.New("wanted attribute names must not be empty") + } + + return builder.attr, err +} + +// MarshalJSON returns the JSON encoding +func (attr *WantedAttribute) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Name string `json:"name"` + Derivation string `json:"derivation,omitempty"` + Constraints []constraintInterface `json:"constraints,omitempty"` + AcceptSelfAsserted bool `json:"accept_self_asserted"` + Optional bool `json:"optional,omitempty"` + }{ + Name: attr.name, + Derivation: attr.derivation, + Constraints: attr.constraints, + AcceptSelfAsserted: attr.acceptSelfAsserted, + Optional: attr.Optional, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_test.go new file mode 100644 index 0000000..0a990d1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/wanted_attribute_test.go @@ -0,0 +1,154 @@ +package digitalidentity + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleWantedAttributeBuilder_WithName() { + builder := (&WantedAttributeBuilder{}).WithName("TEST NAME") + attribute, err := builder.Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(attribute.name) + // Output: TEST NAME +} + +func ExampleWantedAttributeBuilder_WithDerivation() { + attribute, err := (&WantedAttributeBuilder{}). + WithDerivation("TEST DERIVATION"). + WithName("TEST NAME"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(attribute.derivation) + // Output: TEST DERIVATION +} + +func ExampleWantedAttributeBuilder_WithConstraint() { + constraint, err := (&SourceConstraintBuilder{}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithConstraint(&constraint). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","constraints":[{"type":"SOURCE","preferred_sources":{"anchors":[],"soft_preference":false}}],"accept_self_asserted":false} +} + +func ExampleWantedAttributeBuilder_WithAcceptSelfAsserted() { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithAcceptSelfAsserted(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","accept_self_asserted":true} +} + +func ExampleWantedAttributeBuilder_WithAcceptSelfAsserted_false() { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithAcceptSelfAsserted(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","accept_self_asserted":false} +} + +func ExampleWantedAttributeBuilder_optional_true() { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithAcceptSelfAsserted(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + attribute.Optional = true + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","accept_self_asserted":false,"optional":true} +} + +func TestWantedAttributeBuilder_Optional_IsOmittedByDefault(t *testing.T) { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + Build() + if err != nil { + t.Errorf("error: %s", err.Error()) + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + t.Errorf("error: %s", err.Error()) + } + + attributeMap := unmarshalJSONIntoMap(t, marshalledJSON) + + optional := attributeMap["optional"] + + if optional != nil { + t.Errorf("expected `optional` to be nil, but was: '%v'", optional) + } +} + +func unmarshalJSONIntoMap(t *testing.T, byteValue []byte) (result map[string]interface{}) { + var unmarshalled interface{} + err := json.Unmarshal(byteValue, &unmarshalled) + assert.NilError(t, err) + + return unmarshalled.(map[string]interface{}) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response.go new file mode 100644 index 0000000..3274e80 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response.go @@ -0,0 +1,56 @@ +package yotierror + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +var ( + defaultUnknownErrorCodeConst = "UNKNOWN_ERROR" + defaultUnknownErrorMessageConst = "unknown HTTP error" +) + +// Error indicates errors related to the Yoti API. +type Error struct { + Id string `json:"id"` + Status int `json:"status"` + ErrorCode string `json:"error"` + Message string `json:"message"` +} + +func (e Error) Error() string { + return e.ErrorCode + " - " + e.Message +} + +// NewResponseError creates a new Error +func NewResponseError(response *http.Response) *Error { + err := &Error{ + ErrorCode: defaultUnknownErrorCodeConst, + Message: defaultUnknownErrorMessageConst, + } + if response == nil { + return err + } + err.Status = response.StatusCode + if response.Body == nil { + return err + } + defer response.Body.Close() + b, e := io.ReadAll(response.Body) + if e != nil { + err.Message = fmt.Sprintf(defaultUnknownErrorMessageConst+": %q", e) + return err + } + e = json.Unmarshal(b, err) + if e != nil { + err.Message = fmt.Sprintf(defaultUnknownErrorMessageConst+": %q", e) + } + return err +} + +// Temporary indicates this ErrorCode is a temporary ErrorCode +func (e Error) Temporary() bool { + return e.Status >= 500 +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response_test.go new file mode 100644 index 0000000..be2225c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/response_test.go @@ -0,0 +1,68 @@ +package yotierror + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +var ( + expectedErr = Error{ + Id: "8f6a9dfe72128de20909af0d476769b6", + Status: 401, + ErrorCode: "INVALID_REQUEST_SIGNATURE", + Message: "Invalid request signature", + } +) + +func TestError_ShouldReturnFormattedError(t *testing.T) { + jsonBytes := json.RawMessage(`{"id":"8f6a9dfe72128de20909af0d476769b6","status":401,"error":"INVALID_REQUEST_SIGNATURE","message":"Invalid request signature"}`) + + err := NewResponseError( + &http.Response{ + StatusCode: 401, + Body: io.NopCloser(bytes.NewReader(jsonBytes)), + }, + ) + + assert.ErrorIs(t, *err, expectedErr) +} + +func TestError_ShouldReturnFormattedError_ReturnWrappedErrorWhenInvalidJSON(t *testing.T) { + response := &http.Response{ + StatusCode: 400, + Body: io.NopCloser(strings.NewReader("some invalid JSON")), + } + err := NewResponseError( + response, + ) + + assert.ErrorContains(t, err, "unknown HTTP error") +} + +func TestError_ShouldReturnTemporaryForServerError(t *testing.T) { + response := &http.Response{ + StatusCode: 500, + } + err := NewResponseError( + response, + ) + + assert.Check(t, err.Temporary()) +} + +func TestError_ShouldNotReturnTemporaryForClientError(t *testing.T) { + response := &http.Response{ + StatusCode: 400, + } + err := NewResponseError( + response, + ) + + assert.Check(t, !err.Temporary()) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/signed_requests.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/signed_requests.go new file mode 100644 index 0000000..3f91d38 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/digitalidentity/yotierror/signed_requests.go @@ -0,0 +1,8 @@ +package yotierror + +const ( + // InvalidRequestSignature can be returned by any endpoint that requires a signed request. + InvalidRequestSignature = "INVALID_REQUEST_SIGNATURE" + // InvalidAuthHeader can be returned by any endpoint that requires a signed request. + InvalidAuthHeader = "INVALID_AUTH_HEADER" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client.go new file mode 100644 index 0000000..2d8f7ae --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client.go @@ -0,0 +1,438 @@ +package docscan + +import ( + "crypto/rsa" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "strconv" + "strings" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/facecapture" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/retrieve" + "github.com/getyoti/yoti-go-sdk/v3/docscan/supported" + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/requests" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +// Client is responsible for setting up test data in the sandbox instance. +type Client struct { + // SDK ID. This can be found in the Yoti Hub after you have created and activated an application. + SdkID string + // Private Key associated for your application, can be downloaded from the Yoti Hub. + Key *rsa.PrivateKey + // Mockable HTTP Client Interface + HTTPClient requests.HttpClient + // API URL to use. This is not required, and a default will be set if not provided. + apiURL string + // Mockable JSON marshaler + jsonMarshaler jsonMarshaler +} + +var mustNotBeEmptyString = "%s cannot be an empty string" + +// NewClient constructs a Client object +func NewClient(sdkID string, key []byte) (*Client, error) { + if sdkID == "" { + return nil, fmt.Errorf(mustNotBeEmptyString, "SdkID") + } + + decodedKey, err := cryptoutil.ParseRSAKey(key) + if err != nil { + return nil, err + } + + return &Client{ + SdkID: sdkID, + Key: decodedKey, + HTTPClient: http.DefaultClient, + apiURL: getAPIURL(), + }, err +} + +// OverrideAPIURL overrides the default API URL for this Yoti Client +func (c *Client) OverrideAPIURL(apiURL string) { + c.apiURL = apiURL +} + +func getAPIURL() string { + if value, exists := os.LookupEnv("YOTI_DOC_SCAN_API_URL"); exists && value != "" { + return value + } else { + return "https://api.yoti.com/idverify/v1" + } +} + +// CreateSession creates a Doc Scan (IDV) session using the supplied session specification +func (c *Client) CreateSession(sessionSpec *create.SessionSpecification) (*create.SessionResult, error) { + requestBody, err := marshalJSON(c.jsonMarshaler, sessionSpec) + if err != nil { + return nil, err + } + + var request *http.Request + request, err = (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodPost, + BaseURL: c.apiURL, + Endpoint: createSessionPath(), + Headers: requests.JSONHeaders(), + Body: requestBody, + Params: map[string]string{"sdkID": c.SdkID}, + }).Request() + if err != nil { + return nil, err + } + + var response *http.Response + response, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return nil, err + } + + var responseBytes []byte + responseBytes, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + var result create.SessionResult + err = json.Unmarshal(responseBytes, &result) + + return &result, err +} + +// GetSession retrieves the state of a previously created Yoti Doc Scan (IDV) session +func (c *Client) GetSession(sessionID string) (*retrieve.GetSessionResult, error) { + if sessionID == "" { + return nil, fmt.Errorf(mustNotBeEmptyString, "sessionID") + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodGet, + BaseURL: c.apiURL, + Endpoint: getSessionPath(sessionID), + Params: map[string]string{"sdkID": c.SdkID}, + }).Request() + if err != nil { + return nil, err + } + + var response *http.Response + response, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return nil, err + } + + var responseBytes []byte + responseBytes, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + var result retrieve.GetSessionResult + err = json.Unmarshal(responseBytes, &result) + + return &result, err +} + +// DeleteSession deletes a previously created Yoti Doc Scan (IDV) session and all of its related resources +func (c *Client) DeleteSession(sessionID string) error { + if sessionID == "" { + return fmt.Errorf(mustNotBeEmptyString, "sessionID") + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodDelete, + BaseURL: c.apiURL, + Endpoint: deleteSessionPath(sessionID), + Params: map[string]string{"sdkID": c.SdkID}, + }).Request() + if err != nil { + return err + } + + _, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return err + } + + return nil +} + +// GetMediaContent retrieves media related to a Yoti Doc Scan (IDV) session based on the supplied media ID +func (c *Client) GetMediaContent(sessionID, mediaID string) (media.Media, error) { + if sessionID == "" { + return nil, fmt.Errorf(mustNotBeEmptyString, "sessionID") + } + + if mediaID == "" { + return nil, fmt.Errorf(mustNotBeEmptyString, "mediaID") + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodGet, + BaseURL: c.apiURL, + Endpoint: getMediaContentPath(sessionID, mediaID), + Params: map[string]string{"sdkID": c.SdkID}, + }).Request() + if err != nil { + return nil, err + } + + var response *http.Response + response, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return nil, err + } + + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + var responseBytes []byte + responseBytes, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + contentTypes := strings.Split(response.Header.Get("Content-type"), ";") + if len(contentTypes[0]) < 1 { + err = errors.New("unable to parse content type from response") + } + + result := media.NewMedia(contentTypes[0], responseBytes) + + return result, err +} + +// DeleteMediaContent deletes media related to a Yoti Doc Scan (IDV) session based on the supplied media ID +func (c *Client) DeleteMediaContent(sessionID, mediaID string) error { + if sessionID == "" { + return fmt.Errorf(mustNotBeEmptyString, "sessionID") + } + + if mediaID == "" { + return fmt.Errorf(mustNotBeEmptyString, "mediaID") + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodDelete, + BaseURL: c.apiURL, + Endpoint: deleteMediaPath(sessionID, mediaID), + Params: map[string]string{"sdkID": c.SdkID}, + }).Request() + if err != nil { + return err + } + + _, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return err + } + + return nil +} + +// GetSupportedDocuments gets a slice of supported documents (defaults includeNonLatin to false) +func (c *Client) GetSupportedDocuments() (*supported.DocumentsResponse, error) { + return c.GetSupportedDocumentsWithNonLatin(false) +} + +// GetSupportedDocuments gets a slice of supported documents with bool param includeNonLatin +func (c *Client) GetSupportedDocumentsWithNonLatin(includeNonLatin bool) (*supported.DocumentsResponse, error) { + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodGet, + BaseURL: c.apiURL, + Endpoint: getSupportedDocumentsPath(), + Params: map[string]string{"includeNonLatin": strconv.FormatBool(includeNonLatin)}, + }).Request() + if err != nil { + return nil, err + } + + var response *http.Response + response, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return nil, err + } + + var responseBytes []byte + responseBytes, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + var result supported.DocumentsResponse + err = json.Unmarshal(responseBytes, &result) + + return &result, err +} + +// jsonMarshaler is a mockable JSON marshaler +type jsonMarshaler interface { + Marshal(v interface{}) ([]byte, error) +} + +func marshalJSON(jsonMarshaler jsonMarshaler, v interface{}) ([]byte, error) { + if jsonMarshaler != nil { + return jsonMarshaler.Marshal(v) + } + return json.Marshal(v) +} + +func (c *Client) CreateFaceCaptureResource(sessionID string, payload *facecapture.CreateFaceCaptureResourcePayload) (*retrieve.FaceCaptureResourceResponse, error) { + if sessionID == "" { + return nil, fmt.Errorf(mustNotBeEmptyString, "sessionID") + } + + body, err := marshalJSON(c.jsonMarshaler, payload) + if err != nil { + return nil, err + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodPost, + BaseURL: c.apiURL, + Endpoint: fmt.Sprintf("/sessions/%s/resources/face-capture", sessionID), + Params: map[string]string{"sdkID": c.SdkID}, + Headers: requests.JSONHeaders(), + Body: body, + }).Request() + if err != nil { + return nil, err + } + + resp, err := requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return nil, err + } + + var result retrieve.FaceCaptureResourceResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return nil, err + } + + return &result, nil +} + +func (c *Client) UploadFaceCaptureImage(sessionID, resourceID string, payload *facecapture.UploadFaceCaptureImagePayload) error { + if sessionID == "" || resourceID == "" { + return fmt.Errorf("sessionID and resourceID must not be empty") + } + + if err := payload.Prepare(); err != nil { + return fmt.Errorf("failed to prepare multipart payload: %w", err) + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodPut, + BaseURL: c.apiURL, + Endpoint: fmt.Sprintf("/sessions/%s/resources/face-capture/%s/image", sessionID, resourceID), + Params: map[string]string{"sdkID": c.SdkID}, + Body: payload.MultipartFormBody().Bytes(), + Headers: payload.Headers(), + }).Request() + + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + _, err = requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + return err +} + +func (c *Client) GetSessionConfiguration(sessionID string) (*retrieve.SessionConfigurationResponse, error) { + if sessionID == "" { + return nil, fmt.Errorf(mustNotBeEmptyString, "sessionID") + } + + request, err := (&requests.SignedRequest{ + Key: c.Key, + HTTPMethod: http.MethodGet, + BaseURL: c.apiURL, + Endpoint: fmt.Sprintf("/sessions/%s/configuration", sessionID), + Params: map[string]string{"sdkID": c.SdkID}, + }).Request() + if err != nil { + return nil, err + } + + response, err := requests.Execute(c.HTTPClient, request, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return nil, err + } + + var responseBytes []byte + responseBytes, err = io.ReadAll(response.Body) + if err != nil { + return nil, err + } + + var result retrieve.SessionConfigurationResponse + + if err := json.Unmarshal(responseBytes, &result); err != nil { + return nil, fmt.Errorf("failed to unmarshal response: %w", err) + } + + return &result, nil +} + +func (c *Client) AddFaceCaptureResourceToSession(sessionID string, base64Image string) error { + sessionConfig, err := c.GetSessionConfiguration(sessionID) + if err != nil { + return err + } + + if sessionConfig == nil { + return fmt.Errorf("sessionConfig is nil") + } + + capture := sessionConfig.GetCapture() + if capture == nil { + return fmt.Errorf("capture info is missing in sessionConfig") + } + + requirements := capture.GetFaceCaptureResourceRequirements() + if len(requirements) == 0 { + // No face capture resource requirement, nothing to add + return nil + } + + firstRequirement := requirements[0] + if firstRequirement == nil || firstRequirement.ID == "" { + return fmt.Errorf("invalid face capture resource requirement") + } + + payload := facecapture.NewCreateFaceCaptureResourcePayload(firstRequirement.ID) + + resource, err := c.CreateFaceCaptureResource(sessionID, payload) + if err != nil { + return err + } + + // Use the base64Image passed as a parameter + imageBytes, err := base64.StdEncoding.DecodeString(base64Image) + if err != nil { + return err + } + + imagePayload := facecapture.NewUploadFaceCaptureImagePayload("image/png", imageBytes) + return c.UploadFaceCaptureImage(sessionID, resource.ID, imagePayload) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client_test.go new file mode 100644 index 0000000..1eb74e4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/client_test.go @@ -0,0 +1,868 @@ +package docscan + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/facecapture" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/retrieve" + "github.com/getyoti/yoti-go-sdk/v3/docscan/supported" + "github.com/getyoti/yoti-go-sdk/v3/media" + "gotest.tools/v3/assert" +) + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func TestClient_CreateSession(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + var clientSessionTokenTTL int = 100 + var clientSessionToken string = "8c91671a-7194-4ad7-8483-32703b965cfc" + var sessionID string = "c87c4f2a-13fd-4cc8-a0e4-f1637cf32f71" + + jsonResponse := fmt.Sprintf(`{"client_session_token_ttl":%d,"client_session_token":"%s","session_id":"%s"}`, clientSessionTokenTTL, clientSessionToken, sessionID) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusCreated, + Body: io.NopCloser(strings.NewReader(jsonResponse)), + }, nil + }, + } + + var sessionSpec *create.SessionSpecification + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + createSessionResult, err := client.CreateSession(sessionSpec) + assert.NilError(t, err) + + assert.Equal(t, clientSessionTokenTTL, createSessionResult.ClientSessionTokenTTL) + assert.Equal(t, clientSessionToken, createSessionResult.ClientSessionToken) + assert.Equal(t, sessionID, createSessionResult.SessionID) +} + +func TestClient_CreateSession_ShouldReturnJsonMarshalError(t *testing.T) { + client := Client{ + jsonMarshaler: &mockJSONMarshaler{ + marshal: func(v interface{}) ([]byte, error) { + return []byte{}, errors.New("some json error") + }, + }, + } + _, err := client.CreateSession(&create.SessionSpecification{}) + assert.ErrorContains(t, err, "some json error") +} + +func TestClient_CreateSession_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + _, err := client.CreateSession(&create.SessionSpecification{}) + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_CreateSession_ShouldReturnResponseError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadRequest, + }, nil + }, + } + + var sessionSpec *create.SessionSpecification + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + _, err = client.CreateSession(sessionSpec) + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestClient_GetSession(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + var clientSessionTokenTTL int = 100 + var clientSessionToken string = "8c91671a-7194-4ad7-8483-32703b965cfc" + var sessionID string = "c87c4f2a-13fd-4cc8-a0e4-f1637cf32f71" + var userTrackingID string = "user-tracking-id" + var state = "COMPLETED" + jsonResponse := fmt.Sprintf(`{"client_session_token_ttl":%d,"client_session_token":"%s","session_id":"%s","user_tracking_id":"%s","state":"%s"}`, clientSessionTokenTTL, clientSessionToken, sessionID, userTrackingID, state) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(jsonResponse)), + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + getSessionResult, err := client.GetSession(sessionID) + assert.NilError(t, err) + + assert.Equal(t, clientSessionTokenTTL, getSessionResult.ClientSessionTokenTTL) + assert.Equal(t, clientSessionToken, getSessionResult.ClientSessionToken) + assert.Equal(t, sessionID, getSessionResult.SessionID) + assert.Equal(t, userTrackingID, getSessionResult.UserTrackingID) + assert.Equal(t, state, getSessionResult.State) +} + +func TestClient_GetSession_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + _, err := client.GetSession("some-id") + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_GetSession_ShouldReturnResponseError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadRequest, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + _, err = client.GetSession("some-id") + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestClient_GetSession_ShouldReturnJsonError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader("some-invalid-json")), + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + _, err = client.GetSession("some-id") + assert.ErrorContains(t, err, "invalid character") +} + +func TestClient_DeleteSession(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + err = client.DeleteSession("some-session-id") + assert.NilError(t, err) +} + +func TestClient_DeleteSession_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + err := client.DeleteSession("some-id") + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_DeleteSession_ShouldReturnResponseError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadRequest, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + err = client.DeleteSession("some-id") + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestClient_GetMediaContent(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + jpegImage := []byte("value") + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(jpegImage)), + Header: map[string][]string{"Content-Type": {media.ImageTypeJPEG}}, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + result, err := client.GetMediaContent("some-sessionID", "some-mediaID") + assert.NilError(t, err) + + assert.DeepEqual(t, jpegImage, result.Data()) + assert.Equal(t, media.ImageTypeJPEG, result.MIME()) +} + +func TestClient_GetMediaContent_NoContent(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusNoContent, + Header: map[string][]string{}, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + media, err := client.GetMediaContent("some-sessionID", "some-mediaID") + assert.Equal(t, media, nil) + assert.NilError(t, err) +} + +func TestClient_GetMediaContent_NoContentType(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + jpegImage := []byte("value") + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(jpegImage)), + Header: map[string][]string{}, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + _, err = client.GetMediaContent("some-sessionID", "some-mediaID") + assert.ErrorContains(t, err, "unable to parse content type from response") +} + +func TestClient_GetMediaContent_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + _, err := client.GetMediaContent("some-id", "some-media-id") + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_GetMediaContent_ShouldReturnResponseError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadRequest, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + _, err = client.GetMediaContent("some-id", "some-media-id") + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestClient_DeleteMediaContent(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + err = client.DeleteMediaContent("some-session-id", "some-media-id") + assert.NilError(t, err) +} + +func TestClient_DeleteMediaContent_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + err := client.DeleteMediaContent("some-id", "some-media-id") + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_DeleteMediaContent_ShouldReturnResponseError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadRequest, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + err = client.DeleteMediaContent("some-id", "some-media-id") + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestClient_GetSupportedDocuments(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + countryCodeUSA := "USA" + documentTypePassport := "PASSPORT" + isStrictlyLatin := true + documentsResponse := supported.DocumentsResponse{ + SupportedCountries: []*supported.Country{ + { + Code: countryCodeUSA, + SupportedDocuments: []*supported.Document{ + { + Type: documentTypePassport, + IsStrictlyLatin: isStrictlyLatin, + }, + }, + }, + }, + } + + jsonBytes, err := json.Marshal(documentsResponse) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(jsonBytes)), + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + result, err := client.GetSupportedDocuments() + assert.NilError(t, err) + + assert.Equal(t, result.SupportedCountries[0].Code, countryCodeUSA) + assert.Equal(t, result.SupportedCountries[0].SupportedDocuments[0].Type, documentTypePassport) + assert.Equal(t, result.SupportedCountries[0].SupportedDocuments[0].IsStrictlyLatin, isStrictlyLatin) +} + +func TestClient_GetSupportedDocuments_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + _, err := client.GetSupportedDocuments() + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_GetSupportedDocuments_ShouldReturnResponseError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + HTTPClient := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadRequest, + }, nil + }, + } + + client := Client{ + SdkID: "sdkId", + Key: key, + HTTPClient: HTTPClient, + apiURL: "https://apiurl.com", + } + + _, err = client.GetSupportedDocuments() + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestNewClient(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + assert.Equal(t, "sdkID", client.SdkID) +} + +func TestNewClient_EmptySdkID(t *testing.T) { + _, err := NewClient("", []byte("someKey")) + + assert.ErrorContains(t, err, "SdkID cannot be an empty string") +} + +func TestClient_GetSession_EmptySessionID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + _, err = client.GetSession("") + assert.ErrorContains(t, err, "sessionID cannot be an empty string") +} + +func TestClient_DeleteSession_EmptySessionID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + err = client.DeleteSession("") + assert.ErrorContains(t, err, "sessionID cannot be an empty string") +} + +func TestClient_GetMediaContent_EmptySessionID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + _, err = client.GetMediaContent("", "someMediaID") + assert.ErrorContains(t, err, "sessionID cannot be an empty string") +} + +func TestClient_GetMediaContent_EmptyMediaID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + _, err = client.GetMediaContent("someSessionID", "") + assert.ErrorContains(t, err, "mediaID cannot be an empty string") +} + +func TestClient_DeleteMediaContent_EmptySessionID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + err = client.DeleteMediaContent("", "someMediaID") + assert.ErrorContains(t, err, "sessionID cannot be an empty string") +} + +func TestClient_DeleteMediaContent_EmptyMediaID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + err = client.DeleteMediaContent("someSessionID", "") + assert.ErrorContains(t, err, "mediaID cannot be an empty string") +} + +func Test_EmptySdkID(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + _, err = client.GetSession("") + assert.ErrorContains(t, err, "sessionID cannot be an empty string") +} + +func TestNewClient_KeyLoad_Failure(t *testing.T) { + key, err := os.ReadFile("../test/test-key-invalid-format.pem") + assert.NilError(t, err) + + _, err = NewClient("sdkID", key) + + assert.ErrorContains(t, err, "invalid key: not PEM-encoded") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestClient_UsesDefaultApiUrl(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + assert.Equal(t, "https://api.yoti.com/idverify/v1", client.apiURL) +} + +func TestClient_UsesEnvVariable(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + os.Setenv("YOTI_DOC_SCAN_API_URL", "envBaseUrl") + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + assert.Equal(t, "envBaseUrl", client.apiURL) + + os.Unsetenv("YOTI_DOC_SCAN_API_URL") +} + +func TestClient_UsesOverrideApiUrlOverEnvVariable(t *testing.T) { + key, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + os.Setenv("YOTI_DOC_SCAN_API_URL", "envBaseUrl") + + client, err := NewClient("sdkID", key) + assert.NilError(t, err) + + client.OverrideAPIURL("overrideApiURL") + + assert.Equal(t, "overrideApiURL", client.apiURL) + + os.Unsetenv("YOTI_DOC_SCAN_API_URL") +} + +type mockJSONMarshaler struct { + marshal func(v interface{}) ([]byte, error) +} + +func (mock *mockJSONMarshaler) Marshal(v interface{}) ([]byte, error) { + if mock.marshal != nil { + return mock.marshal(v) + } + return nil, nil +} + +type testJSONMarshaler struct{} + +func (m testJSONMarshaler) Marshal(v interface{}) ([]byte, error) { + return json.Marshal(v) +} + +func TestClient_CreateFaceCaptureResource(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + + expected := &retrieve.FaceCaptureResourceResponse{ID: "resource-id"} + expectedBytes, _ := json.Marshal(expected) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(r *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewReader(expectedBytes)), + }, nil + }, + }, + apiURL: "https://example.com", + SdkID: "sdk-id", + jsonMarshaler: testJSONMarshaler{}, + } + + payload := facecapture.NewCreateFaceCaptureResourcePayload("requirement-id") + result, err := client.CreateFaceCaptureResource("session-id", payload) + + assert.NilError(t, err) + assert.Equal(t, result.ID, expected.ID) +} + +func TestClient_UploadFaceCaptureImage(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + + image := []byte("test-image") + payload := facecapture.NewUploadFaceCaptureImagePayload("image/png", image) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(r *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 204, + Body: io.NopCloser(bytes.NewReader([]byte{})), + }, nil + }, + }, + apiURL: "https://example.com", + SdkID: "sdk-id", + } + + err := client.UploadFaceCaptureImage("session-id", "resource-id", payload) + assert.NilError(t, err) +} + +func TestClient_GetSessionConfiguration(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + + expected := &retrieve.SessionConfigurationResponse{} + expectedBytes, _ := json.Marshal(expected) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(r *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewReader(expectedBytes)), + }, nil + }, + }, + apiURL: "https://example.com", + SdkID: "sdk-id", + } + + result, err := client.GetSessionConfiguration("session-id") + assert.NilError(t, err) + assert.DeepEqual(t, result, expected) +} + +func TestClient_GetSessionConfiguration_FailsOnEmptySessionID(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + client := Client{Key: key} + + _, err := client.GetSessionConfiguration("") + assert.Error(t, err, "sessionID cannot be an empty string") +} + +func TestClient_CreateFaceCaptureResource_FailsOnEmptySessionID(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + client := Client{Key: key} + + payload := facecapture.NewCreateFaceCaptureResourcePayload("requirement-id") + _, err := client.CreateFaceCaptureResource("", payload) + assert.Error(t, err, "sessionID cannot be an empty string") +} + +func TestClient_UploadFaceCaptureImage_FailsOnEmptyIDs(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + client := Client{Key: key} + + payload := facecapture.NewUploadFaceCaptureImagePayload("image/png", []byte("img")) + + err := client.UploadFaceCaptureImage("", "resource-id", payload) + assert.Error(t, err, "sessionID and resourceID must not be empty") + + err = client.UploadFaceCaptureImage("session-id", "", payload) + assert.Error(t, err, "sessionID and resourceID must not be empty") +} + +func TestClient_AddFaceCaptureResourceToSession_HappyPath(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + mockBase64Image := "aW1hZ2U=" // "image" + + mockConfigResponse := `{"capture":{"required_resources":[{"type":"FACE_CAPTURE","id":"requirement-id-123"}]}}` + mockResourceResponse := `{"id":"resource-id-456"}` + + var getConfigCalled, createResourceCalled, uploadImageCalled bool + + mockClient := &mockHTTPClient{ + do: func(req *http.Request) (*http.Response, error) { + if strings.Contains(req.URL.Path, "/configuration") { + getConfigCalled = true + return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(mockConfigResponse))}, nil + } + if req.Method == http.MethodPost && strings.Contains(req.URL.Path, "/face-capture") { + createResourceCalled = true + return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(mockResourceResponse))}, nil + } + if req.Method == http.MethodPut && strings.Contains(req.URL.Path, "/image") { + uploadImageCalled = true + return &http.Response{StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte{}))}, nil + } + return nil, fmt.Errorf("unexpected request: %s %s", req.Method, req.URL.Path) + }, + } + + client := Client{ + Key: key, + HTTPClient: mockClient, + apiURL: "https://example.com", + SdkID: "sdk-id", + jsonMarshaler: testJSONMarshaler{}, + } + + err := client.AddFaceCaptureResourceToSession("session-id", mockBase64Image) + + assert.NilError(t, err) + assert.Assert(t, getConfigCalled, "GetSessionConfiguration should have been called") + assert.Assert(t, createResourceCalled, "CreateFaceCaptureResource should have been called") + assert.Assert(t, uploadImageCalled, "UploadFaceCaptureImage should have been called") +} + +func TestClient_AddFaceCaptureResourceToSession_GetConfigFails(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + expectedErr := errors.New("failed to get config") + + mockClient := &mockHTTPClient{ + do: func(req *http.Request) (*http.Response, error) { + return nil, expectedErr + }, + } + + client := Client{Key: key, HTTPClient: mockClient, apiURL: "https://example.com", SdkID: "sdk-id"} + err := client.AddFaceCaptureResourceToSession("session-id", "aW1hZ2U=") + + assert.ErrorIs(t, err, expectedErr) +} + +func TestClient_AddFaceCaptureResourceToSession_NoRequirements(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + mockConfigResponse := `{"capture":{"required_resources":[]}}` + + mockClient := &mockHTTPClient{ + do: func(req *http.Request) (*http.Response, error) { + if strings.Contains(req.URL.Path, "/configuration") { + return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(mockConfigResponse))}, nil + } + t.Fatalf("unexpected request was made: %s %s", req.Method, req.URL.Path) + return nil, nil + }, + } + client := Client{Key: key, HTTPClient: mockClient, apiURL: "https://example.com", SdkID: "sdk-id"} + + err := client.AddFaceCaptureResourceToSession("session-id", "aW1hZ2U=") + assert.NilError(t, err) +} + +func TestClient_AddFaceCaptureResourceToSession_InvalidBase64(t *testing.T) { + key, _ := rsa.GenerateKey(rand.Reader, 1024) + mockConfigResponse := `{"capture":{"required_resources":[{"type":"FACE_CAPTURE","id":"requirement-id-123"}]}}` + mockResourceResponse := `{"id":"resource-id-456"}` + + mockClient := &mockHTTPClient{ + do: func(req *http.Request) (*http.Response, error) { + if strings.Contains(req.URL.Path, "/configuration") { + return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(mockConfigResponse))}, nil + } + if req.Method == http.MethodPost && strings.Contains(req.URL.Path, "/face-capture") { + return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(mockResourceResponse))}, nil + } + t.Fatalf("unexpected request was made: %s %s", req.Method, req.URL.Path) + return nil, nil + }, + } + client := Client{ + Key: key, + HTTPClient: mockClient, + apiURL: "https://example.com", + SdkID: "sdk-id", + jsonMarshaler: testJSONMarshaler{}, + } + + err := client.AddFaceCaptureResourceToSession("session-id", "this is not base64") + assert.ErrorContains(t, err, "illegal base64 data") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/constants/constants.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/constants/constants.go new file mode 100644 index 0000000..5fbd0f7 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/constants/constants.go @@ -0,0 +1,45 @@ +package constants + +const ( + IDDocumentAuthenticity string = "ID_DOCUMENT_AUTHENTICITY" + IDDocumentComparison string = "ID_DOCUMENT_COMPARISON" + IDDocumentTextDataCheck string = "ID_DOCUMENT_TEXT_DATA_CHECK" + IDDocumentTextDataExtraction string = "ID_DOCUMENT_TEXT_DATA_EXTRACTION" + IDDocumentFaceMatch string = "ID_DOCUMENT_FACE_MATCH" + SupplementaryDocumentTextDataCheck string = "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK" + ThirdPartyIdentityCheck string = "THIRD_PARTY_IDENTITY" + SupplementaryDocumentTextDataExtraction string = "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION" + WatchlistScreening string = "WATCHLIST_SCREENING" + WatchlistAdvancedCA string = "WATCHLIST_ADVANCED_CA" + + WithYotiAccounts = "WITH_YOTI_ACCOUNT" + WithCustomAccount = "WITH_CUSTOM_ACCOUNT" + TypeList = "TYPE_LIST" + Profiles = "PROFILE" + Exact = "EXACT" + Fuzzy = "FUZZY" + + Liveness string = "LIVENESS" + Zoom string = "ZOOM" + Static string = "STATIC" + + Camera string = "CAMERA" + CameraAndUpload string = "CAMERA_AND_UPLOAD" + + ResourceUpdate string = "RESOURCE_UPDATE" + TaskCompletion string = "TASK_COMPLETION" + CheckCompletion string = "CHECK_COMPLETION" + SessionCompletion string = "SESSION_COMPLETION" + + Sanctions string = "SANCTIONS" + AdverseMedia string = "ADVERSE-MEDIA" + + Always string = "ALWAYS" + Fallback string = "FALLBACK" + Never string = "NEVER" + + ProofOfAddress string = "PROOF_OF_ADDRESS" + Early string = "EARLY" + JustInTime string = "JUST_IN_TIME" + FaceComparison string = "FACE_COMPARISON" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/endpoint.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/endpoint.go new file mode 100644 index 0000000..77e9d9c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/endpoint.go @@ -0,0 +1,27 @@ +package docscan + +import "fmt" + +func createSessionPath() string { + return "/sessions" +} + +func getSessionPath(sessionID string) string { + return fmt.Sprintf("/sessions/%s", sessionID) +} + +func deleteSessionPath(sessionID string) string { + return getSessionPath(sessionID) +} + +func getMediaContentPath(sessionID string, mediaID string) string { + return fmt.Sprintf("/sessions/%s/media/%s/content", sessionID, mediaID) +} + +func deleteMediaPath(sessionID string, mediaID string) string { + return getMediaContentPath(sessionID, mediaID) +} + +func getSupportedDocumentsPath() string { + return "/supported-documents" +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client.go new file mode 100644 index 0000000..f86ecc9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client.go @@ -0,0 +1,132 @@ +package sandbox + +import ( + "crypto/rsa" + "encoding/json" + "net/http" + "os" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request" + "github.com/getyoti/yoti-go-sdk/v3/requests" + yotirequest "github.com/getyoti/yoti-go-sdk/v3/requests" +) + +type jsonMarshaler interface { + Marshal(v interface{}) ([]byte, error) +} + +// Client is responsible for setting up test data in the sandbox instance. +type Client struct { + // SDK ID. This can be found in the Yoti Hub after you have created and activated an application. + SdkID string + // Private Key associated for your application, can be downloaded from the Yoti Hub. + Key *rsa.PrivateKey + // Mockable HTTP Client Interface + HTTPClient requests.HttpClient + // API URL to use. This is not required, and a default will be set if not provided. + apiURL string + // Mockable JSON marshaler + jsonMarshaler jsonMarshaler +} + +// NewClient constructs a Client object +func NewClient(sdkID string, key []byte) (*Client, error) { + decodedKey, err := cryptoutil.ParseRSAKey(key) + + if err != nil { + return nil, err + } + + return &Client{ + SdkID: sdkID, + Key: decodedKey, + }, err +} + +// OverrideAPIURL overrides the default API URL for this Yoti Client +func (client *Client) OverrideAPIURL(apiURL string) { + client.apiURL = apiURL +} + +func (client *Client) getAPIURL() string { + if client.apiURL == "" { + if value, exists := os.LookupEnv("YOTI_DOC_SCAN_API_URL"); exists && value != "" { + client.apiURL = value + } else { + client.apiURL = "https://api.yoti.com/sandbox/idverify/v1" + } + } + return client.apiURL +} + +func (client *Client) getHTTPClient() requests.HttpClient { + if client.HTTPClient != nil { + return client.HTTPClient + } + return http.DefaultClient +} + +func (client *Client) marshalJSON(v interface{}) ([]byte, error) { + if client.jsonMarshaler != nil { + return client.jsonMarshaler.Marshal(v) + } + return json.Marshal(v) +} + +func (client *Client) makeConfigureResponseRequest(request *http.Request) error { + _, err := requests.Execute(client.getHTTPClient(), request) + + if err != nil { + return err + } + + return nil +} + +// ConfigureSessionResponse configures the response for the session +func (client *Client) ConfigureSessionResponse(sessionID string, responseConfig *request.ResponseConfig) error { + requestEndpoint := "/sessions/" + sessionID + "/response-config" + requestBody, err := client.marshalJSON(responseConfig) + if err != nil { + return err + } + + signedRequest, err := (&yotirequest.SignedRequest{ + Key: client.Key, + HTTPMethod: http.MethodPut, + BaseURL: client.getAPIURL(), + Endpoint: requestEndpoint, + Headers: yotirequest.JSONHeaders(), + Body: requestBody, + Params: map[string]string{"sdkId": client.SdkID}, + }).Request() + if err != nil { + return err + } + + return client.makeConfigureResponseRequest(signedRequest) +} + +// ConfigureApplicationResponse configures the response for the application +func (client *Client) ConfigureApplicationResponse(responseConfig *request.ResponseConfig) error { + requestEndpoint := "/apps/" + client.SdkID + "/response-config" + requestBody, err := client.marshalJSON(responseConfig) + if err != nil { + return err + } + + signedRequest, err := (&yotirequest.SignedRequest{ + Key: client.Key, + HTTPMethod: http.MethodPut, + BaseURL: client.getAPIURL(), + Endpoint: requestEndpoint, + Headers: yotirequest.JSONHeaders(), + Body: requestBody, + }).Request() + if err != nil { + return err + } + + return client.makeConfigureResponseRequest(signedRequest) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client_test.go new file mode 100644 index 0000000..a4876c2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/client_test.go @@ -0,0 +1,343 @@ +package sandbox + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "encoding/json" + "errors" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" + "gotest.tools/v3/assert" +) + +func TestClient_httpClient_ShouldReturnDefaultClient(t *testing.T) { + client := Client{} + assert.Check(t, client.getHTTPClient() != nil) +} + +func TestClient_ConfigureSessionResponse_ShouldReturnErrorIfNotCreated(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 400, + Body: io.NopCloser(strings.NewReader("")), + }, nil + }, + }, + } + err = client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.ErrorContains(t, err, "400: unknown HTTP error") +} + +func TestClient_ConfigureSessionResponse_ShouldReturnFormattedErrorWithResponse(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + jsonBytes, err := json.Marshal(yotierror.DataObject{ + Code: "SOME_CODE", + Message: "some message", + }) + assert.NilError(t, err) + + response := &http.Response{ + StatusCode: 400, + Body: io.NopCloser(bytes.NewReader(jsonBytes)), + } + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return response, nil + }, + }, + } + err = client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.ErrorContains(t, err, "400: SOME_CODE - some message") + + errorResponse := err.(*yotierror.Error).Response + assert.Equal(t, response, errorResponse) + + body, err := io.ReadAll(errorResponse.Body) + assert.NilError(t, err) + assert.Equal(t, string(body), string(jsonBytes)) +} + +func TestClient_ConfigureSessionResponse_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_ConfigureSessionResponse_ShouldReturnJsonError(t *testing.T) { + client := Client{ + jsonMarshaler: &mockJSONMarshaler{ + marshal: func(v interface{}) ([]byte, error) { + return []byte{}, errors.New("some json error") + }, + }, + } + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.ErrorContains(t, err, "some json error") +} + +func TestNewClient_ConfigureSessionResponse_Success(t *testing.T) { + key, err := os.ReadFile("../../test/test-key.pem") + assert.NilError(t, err) + + client, err := NewClient("ClientSDKID", key) + assert.NilError(t, err) + + client.HTTPClient = &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + }, nil + }, + } + + responseErr := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, responseErr) +} + +func TestNewClient_KeyLoad_Failure(t *testing.T) { + key, err := os.ReadFile("../../test/test-key-invalid-format.pem") + assert.NilError(t, err) + + _, err = NewClient("", key) + + assert.ErrorContains(t, err, "invalid key: not PEM-encoded") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestClient_ConfigureSessionResponse_Success(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + }, nil + }, + }, + } + err = client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, err) +} + +func TestClient_ConfigureSessionResponse_ShouldReturnHttpClientError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("some error") + }, + }, + } + err = client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.ErrorContains(t, err, "some error") +} + +func TestClient_ConfigureApplicationResponse_ShouldReturnErrorIfNotCreated(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 401, + Body: io.NopCloser(strings.NewReader("")), + }, nil + }, + }, + } + err = client.ConfigureApplicationResponse(&request.ResponseConfig{}) + assert.ErrorContains(t, err, "401: unknown HTTP error") +} + +func TestClient_ConfigureApplicationResponse_ShouldReturnMissingKeyError(t *testing.T) { + client := Client{} + err := client.ConfigureApplicationResponse(&request.ResponseConfig{}) + assert.ErrorContains(t, err, "missing private key") +} + +func TestClient_ConfigureApplicationResponse_ShouldReturnJsonError(t *testing.T) { + client := Client{ + jsonMarshaler: &mockJSONMarshaler{ + marshal: func(v interface{}) ([]byte, error) { + return []byte{}, errors.New("some json error") + }, + }, + } + err := client.ConfigureApplicationResponse(&request.ResponseConfig{}) + assert.ErrorContains(t, err, "some json error") +} + +func TestClient_ConfigureApplicationResponse_Success(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + }, nil + }, + }, + } + err = client.ConfigureApplicationResponse(&request.ResponseConfig{}) + assert.NilError(t, err) +} + +func TestClient_ConfigureApplicationResponse_ShouldReturnHttpClientError(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("some error") + }, + }, + } + err = client.ConfigureApplicationResponse(&request.ResponseConfig{}) + assert.ErrorContains(t, err, "some error") +} + +func TestClient_ConfigureSessionResponseUsesConstructorApiUrlOverEnvVariable(t *testing.T) { + client := createSandboxClient(t, "constuctorApiURL") + os.Setenv("YOTI_DOC_SCAN_API_URL", "envBaseUrl") + + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, err) + + assert.Equal(t, "constuctorApiURL", client.getAPIURL()) +} + +func TestClient_ConfigureSessionResponseUsesOverrideApiUrlOverEnvVariable(t *testing.T) { + client := createSandboxClient(t, "") + client.OverrideAPIURL("overrideApiURL") + os.Setenv("YOTI_DOC_SCAN_API_URL", "envBaseUrl") + + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, err) + + assert.Equal(t, "overrideApiURL", client.getAPIURL()) +} + +func TestClient_ConfigureSessionResponseUsesEnvVariable(t *testing.T) { + client := createSandboxClient(t, "") + + os.Setenv("YOTI_DOC_SCAN_API_URL", "envApiURL") + + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, err) + + assert.Equal(t, "envApiURL", client.getAPIURL()) +} + +func TestClient_ConfigureSessionResponseUsesDefaultUrlAsFallbackWithEmptyEnvValue(t *testing.T) { + os.Setenv("YOTI_DOC_SCAN_API_URL", "") + + client := createSandboxClient(t, "") + + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, err) + + assert.Equal(t, "https://api.yoti.com/sandbox/idverify/v1", client.getAPIURL()) +} + +func TestClient_ConfigureSessionResponseUsesDefaultUrlAsFallbackWithNoEnvValue(t *testing.T) { + os.Unsetenv("YOTI_DOC_SCAN_API_URL") + + client := createSandboxClient(t, "") + + err := client.ConfigureSessionResponse("some_session_id", &request.ResponseConfig{}) + assert.NilError(t, err) + + assert.Equal(t, "https://api.yoti.com/sandbox/idverify/v1", client.getAPIURL()) +} + +func createSandboxClient(t *testing.T, constructorApiURL string) (client Client) { + keyBytes, fileErr := os.ReadFile("../../test/test-key.pem") + assert.NilError(t, fileErr) + + pemFile, parseErr := cryptoutil.ParseRSAKey(keyBytes) + assert.NilError(t, parseErr) + + if constructorApiURL == "" { + return Client{ + Key: pemFile, + SdkID: "ClientSDKID", + HTTPClient: mockHTTPClientCreatedResponse(), + } + } + + return Client{ + Key: pemFile, + SdkID: "ClientSDKID", + HTTPClient: mockHTTPClientCreatedResponse(), + apiURL: constructorApiURL, + } + +} + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func mockHTTPClientCreatedResponse() *mockHTTPClient { + return &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + }, nil + }, + } +} + +type mockJSONMarshaler struct { + marshal func(v interface{}) ([]byte, error) +} + +func (mock *mockJSONMarshaler) Marshal(v interface{}) ([]byte, error) { + if mock.marshal != nil { + return mock.marshal(v) + } + return nil, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/check.go new file mode 100644 index 0000000..e2f2a22 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/check.go @@ -0,0 +1,42 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" +) + +type check struct { + Result checkResult `json:"result"` +} + +type checkBuilder struct { + recommendation *report.Recommendation + breakdowns []*report.Breakdown +} + +type checkResult struct { + Report checkReport `json:"report"` +} + +type checkReport struct { + Recommendation *report.Recommendation `json:"recommendation,omitempty"` + Breakdown []*report.Breakdown `json:"breakdown,omitempty"` +} + +func (b *checkBuilder) withRecommendation(recommendation *report.Recommendation) { + b.recommendation = recommendation +} + +func (b *checkBuilder) withBreakdown(breakdown *report.Breakdown) { + b.breakdowns = append(b.breakdowns, breakdown) +} + +func (b *checkBuilder) build() *check { + return &check{ + Result: checkResult{ + Report: checkReport{ + Recommendation: b.recommendation, + Breakdown: b.breakdowns, + }, + }, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check.go new file mode 100644 index 0000000..587aabf --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check.go @@ -0,0 +1,46 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// DocumentAuthenticityCheck Represents a document authenticity check +type DocumentAuthenticityCheck struct { + *documentCheck +} + +// DocumentAuthenticityCheckBuilder builds a DocumentAuthenticityCheck +type DocumentAuthenticityCheckBuilder struct { + documentCheckBuilder +} + +// NewDocumentAuthenticityCheckBuilder creates a new DocumentAuthenticityCheckBuilder +func NewDocumentAuthenticityCheckBuilder() *DocumentAuthenticityCheckBuilder { + return &DocumentAuthenticityCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *DocumentAuthenticityCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *DocumentAuthenticityCheckBuilder { + b.documentCheckBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *DocumentAuthenticityCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *DocumentAuthenticityCheckBuilder { + b.documentCheckBuilder.withBreakdown(breakdown) + return b +} + +// WithDocumentFilter adds a document filter to the check +func (b *DocumentAuthenticityCheckBuilder) WithDocumentFilter(filter *filter.DocumentFilter) *DocumentAuthenticityCheckBuilder { + b.documentCheckBuilder.withDocumentFilter(filter) + return b +} + +// Build creates a new DocumentAuthenticityCheck +func (b *DocumentAuthenticityCheckBuilder) Build() (*DocumentAuthenticityCheck, error) { + return &DocumentAuthenticityCheck{ + documentCheck: b.documentCheckBuilder.build(), + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check_test.go new file mode 100644 index 0000000..0610421 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_authenticity_check_test.go @@ -0,0 +1,53 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleDocumentAuthenticityCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewDocumentAuthenticityCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + WithDocumentFilter(docFilter). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}},"document_filter":{"document_types":[],"country_codes":[]}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_check.go new file mode 100644 index 0000000..42caadc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_check.go @@ -0,0 +1,26 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +type documentCheck struct { + *check + DocumentFilter *filter.DocumentFilter `json:"document_filter,omitempty"` +} + +type documentCheckBuilder struct { + checkBuilder + documentFilter *filter.DocumentFilter +} + +func (b *documentCheckBuilder) withDocumentFilter(filter *filter.DocumentFilter) { + b.documentFilter = filter +} + +func (b *documentCheckBuilder) build() *documentCheck { + return &documentCheck{ + check: b.checkBuilder.build(), + DocumentFilter: b.documentFilter, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check.go new file mode 100644 index 0000000..5c3c1b2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check.go @@ -0,0 +1,46 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// DocumentFaceMatchCheck represents a face match check +type DocumentFaceMatchCheck struct { + *documentCheck +} + +// DocumentFaceMatchCheckBuilder builds a DocumentFaceMatchCheck +type DocumentFaceMatchCheckBuilder struct { + documentCheckBuilder +} + +// NewDocumentFaceMatchCheckBuilder creates a new DocumentFaceMatchCheckBuilder +func NewDocumentFaceMatchCheckBuilder() *DocumentFaceMatchCheckBuilder { + return &DocumentFaceMatchCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *DocumentFaceMatchCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *DocumentFaceMatchCheckBuilder { + b.documentCheckBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *DocumentFaceMatchCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *DocumentFaceMatchCheckBuilder { + b.documentCheckBuilder.withBreakdown(breakdown) + return b +} + +// WithDocumentFilter adds a document filter to the check +func (b *DocumentFaceMatchCheckBuilder) WithDocumentFilter(filter *filter.DocumentFilter) *DocumentFaceMatchCheckBuilder { + b.documentCheckBuilder.withDocumentFilter(filter) + return b +} + +// Build creates a new DocumentFaceMatchCheck +func (b *DocumentFaceMatchCheckBuilder) Build() (*DocumentFaceMatchCheck, error) { + return &DocumentFaceMatchCheck{ + documentCheck: b.documentCheckBuilder.build(), + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check_test.go new file mode 100644 index 0000000..32005f8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_face_match_check_test.go @@ -0,0 +1,53 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleDocumentFaceMatchCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewDocumentFaceMatchCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + WithDocumentFilter(docFilter). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}},"document_filter":{"document_types":[],"country_codes":[]}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check.go new file mode 100644 index 0000000..88aadcd --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check.go @@ -0,0 +1,75 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// DocumentTextDataCheck represents a document text data check +type DocumentTextDataCheck struct { + Result DocumentTextDataCheckResult `json:"result"` + *documentCheck +} + +// DocumentTextDataCheckBuilder builds a DocumentTextDataCheck +type DocumentTextDataCheckBuilder struct { + documentCheckBuilder + documentFields map[string]interface{} +} + +// DocumentTextDataCheckResult represent a document text data check result +type DocumentTextDataCheckResult struct { + checkResult + DocumentFields map[string]interface{} `json:"document_fields,omitempty"` +} + +// NewDocumentTextDataCheckBuilder builds a new DocumentTextDataCheckResult +func NewDocumentTextDataCheckBuilder() *DocumentTextDataCheckBuilder { + return &DocumentTextDataCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *DocumentTextDataCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *DocumentTextDataCheckBuilder { + b.documentCheckBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *DocumentTextDataCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *DocumentTextDataCheckBuilder { + b.documentCheckBuilder.withBreakdown(breakdown) + return b +} + +// WithDocumentFilter adds a document filter to the check +func (b *DocumentTextDataCheckBuilder) WithDocumentFilter(filter *filter.DocumentFilter) *DocumentTextDataCheckBuilder { + b.documentCheckBuilder.withDocumentFilter(filter) + return b +} + +// WithDocumentField adds a document field to the text data check +func (b *DocumentTextDataCheckBuilder) WithDocumentField(key string, value interface{}) *DocumentTextDataCheckBuilder { + if b.documentFields == nil { + b.documentFields = make(map[string]interface{}) + } + b.documentFields[key] = value + return b +} + +// WithDocumentFields sets document fields +func (b *DocumentTextDataCheckBuilder) WithDocumentFields(documentFields map[string]interface{}) *DocumentTextDataCheckBuilder { + b.documentFields = documentFields + return b +} + +// Build creates a new DocumentTextDataCheck +func (b *DocumentTextDataCheckBuilder) Build() (*DocumentTextDataCheck, error) { + docCheck := b.documentCheckBuilder.build() + + return &DocumentTextDataCheck{ + documentCheck: docCheck, + Result: DocumentTextDataCheckResult{ + checkResult: docCheck.Result, + DocumentFields: b.documentFields, + }, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check_test.go new file mode 100644 index 0000000..1924d85 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/document_text_data_check_test.go @@ -0,0 +1,98 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleDocumentTextDataCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewDocumentTextDataCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + WithDocumentFilter(docFilter). + WithDocumentField("some-key", "some-value"). + WithDocumentField("some-other-key", map[string]string{ + "some-nested-key": "some-nested-value", + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]},"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}},"document_filter":{"document_types":[],"country_codes":[]}} +} + +func ExampleDocumentTextDataCheckBuilder_WithDocumentFields() { + check, err := NewDocumentTextDataCheckBuilder(). + WithDocumentFields(map[string]interface{}{ + "some-key": "some-value", + "some-other-key": map[string]string{ + "some-nested-key": "some-nested-value", + }, + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{},"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}}} +} + +func ExampleDocumentTextDataCheckBuilder_minimal() { + check, err := NewDocumentTextDataCheckBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{}}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check.go new file mode 100644 index 0000000..1a122d1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check.go @@ -0,0 +1,49 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// IDDocumentComparisonCheck Represents a document authenticity check +type IDDocumentComparisonCheck struct { + *check + SecondaryDocumentFilter *filter.DocumentFilter `json:"secondary_document_filter,omitempty"` +} + +// IDDocumentComparisonCheckBuilder builds a IDDocumentComparisonCheck +type IDDocumentComparisonCheckBuilder struct { + checkBuilder + secondaryDocumentFilter *filter.DocumentFilter +} + +// NewIDDocumentComparisonCheckBuilder creates a new IDDocumentComparisonCheckBuilder +func NewIDDocumentComparisonCheckBuilder() *IDDocumentComparisonCheckBuilder { + return &IDDocumentComparisonCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *IDDocumentComparisonCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *IDDocumentComparisonCheckBuilder { + b.checkBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *IDDocumentComparisonCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *IDDocumentComparisonCheckBuilder { + b.checkBuilder.withBreakdown(breakdown) + return b +} + +// WithSecondaryDocumentFilter adds a secondary document filter to the check +func (b *IDDocumentComparisonCheckBuilder) WithSecondaryDocumentFilter(filter *filter.DocumentFilter) *IDDocumentComparisonCheckBuilder { + b.secondaryDocumentFilter = filter + return b +} + +// Build creates a new IDDocumentComparisonCheck +func (b *IDDocumentComparisonCheckBuilder) Build() (*IDDocumentComparisonCheck, error) { + return &IDDocumentComparisonCheck{ + check: b.checkBuilder.build(), + SecondaryDocumentFilter: b.secondaryDocumentFilter, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check_test.go new file mode 100644 index 0000000..ed9af75 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/id_document_comparison_check_test.go @@ -0,0 +1,73 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleIDDocumentComparisonCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + WithDetail("some_name", "some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + docFilter, err := filter.NewDocumentFilterBuilder(). + WithCountryCode("AUS"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + idDocumentComparisonCheck, err := NewIDDocumentComparisonCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + WithSecondaryDocumentFilter(docFilter). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(idDocumentComparisonCheck) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[{"name":"some_name","value":"some_value"}]}]}},"secondary_document_filter":{"document_types":[],"country_codes":["AUS"]}} +} + +func ExampleIDDocumentComparisonCheckBuilder_minimal() { + idDocumentComparisonCheck, err := NewIDDocumentComparisonCheckBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(idDocumentComparisonCheck) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{}}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/liveness_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/liveness_check.go new file mode 100644 index 0000000..21f9e4e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/liveness_check.go @@ -0,0 +1,24 @@ +package check + +// LivenessCheck represents a liveness check +type LivenessCheck struct { + *check + LivenessType string `json:"liveness_type"` +} + +type livenessCheckBuilder struct { + checkBuilder + livenessType string +} + +func (b *livenessCheckBuilder) withLivenessType(livenessType string) *livenessCheckBuilder { + b.livenessType = livenessType + return b +} + +func (b *livenessCheckBuilder) build() *LivenessCheck { + return &LivenessCheck{ + LivenessType: b.livenessType, + check: b.checkBuilder.build(), + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown.go new file mode 100644 index 0000000..a681417 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown.go @@ -0,0 +1,72 @@ +package report + +import ( + "errors" +) + +// Breakdown describes a breakdown on check +type Breakdown struct { + SubCheck string `json:"sub_check"` + Result string `json:"result"` + Details []*detail `json:"details"` +} + +// BreakdownBuilder builds a Breakdown +type BreakdownBuilder struct { + subCheck string + result string + details []*detail +} + +// Detail is an individual breakdown detail +type detail struct { + Name string `json:"name"` + Value string `json:"value"` +} + +// NewBreakdownBuilder creates a new BreakdownBuilder +func NewBreakdownBuilder() *BreakdownBuilder { + return &BreakdownBuilder{ + details: []*detail{}, + } +} + +// WithSubCheck sets the sub check of a Breakdown +func (b *BreakdownBuilder) WithSubCheck(subCheck string) *BreakdownBuilder { + b.subCheck = subCheck + return b +} + +// WithResult sets the result of a Breakdown +func (b *BreakdownBuilder) WithResult(result string) *BreakdownBuilder { + b.result = result + return b +} + +// WithDetail sets the Detail of a Breakdown +func (b *BreakdownBuilder) WithDetail(name string, value string) *BreakdownBuilder { + b.details = append(b.details, &detail{ + Name: name, + Value: value, + }) + return b +} + +// Build creates a new Breakdown +func (b *BreakdownBuilder) Build() (*Breakdown, error) { + breakdown := &Breakdown{ + SubCheck: b.subCheck, + Result: b.result, + Details: b.details, + } + + if len(breakdown.SubCheck) == 0 { + return nil, errors.New("Sub Check cannot be empty") + } + + if len(breakdown.Result) == 0 { + return nil, errors.New("Result cannot be empty") + } + + return breakdown, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown_test.go new file mode 100644 index 0000000..98af0f0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/breakdown_test.go @@ -0,0 +1,80 @@ +package report + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func Test_BreakdownBuilder(t *testing.T) { + breakdown, err := NewBreakdownBuilder(). + WithSubCheck("some_sub_check"). + WithResult("some_result"). + WithDetail("some_name", "some_value"). + Build() + + assert.NilError(t, err) + assert.Equal(t, breakdown.SubCheck, "some_sub_check") + assert.Equal(t, breakdown.Result, "some_result") + assert.Equal(t, breakdown.Details[0].Name, "some_name") + assert.Equal(t, breakdown.Details[0].Value, "some_value") +} + +func Test_BreakdownBuilder_ShouldRequireSubCheck(t *testing.T) { + _, err := NewBreakdownBuilder(). + WithResult("some_result"). + Build() + + assert.Error(t, err, "Sub Check cannot be empty") +} + +func Test_BreakdownBuilder_ShouldRequireResult(t *testing.T) { + _, err := NewBreakdownBuilder(). + WithSubCheck("some_sub_check"). + Build() + + assert.Error(t, err, "Result cannot be empty") +} + +func ExampleBreakdownBuilder() { + breakdown, err := NewBreakdownBuilder(). + WithSubCheck("some_sub_check"). + WithResult("some_result"). + WithDetail("some_name", "some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(breakdown) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"sub_check":"some_sub_check","result":"some_result","details":[{"name":"some_name","value":"some_value"}]} +} + +func ExampleBreakdownBuilder_minimal() { + breakdown, err := NewBreakdownBuilder(). + WithSubCheck("some_sub_check"). + WithResult("some_result"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(breakdown) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"sub_check":"some_sub_check","result":"some_result","details":[]} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation.go new file mode 100644 index 0000000..b06e6fe --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation.go @@ -0,0 +1,57 @@ +package report + +import ( + "errors" +) + +// Recommendation describes a recommendation on check +type Recommendation struct { + Value string `json:"value"` + Reason string `json:"reason,omitempty"` + RecoverySuggestion string `json:"recovery_suggestion,omitempty"` +} + +// RecommendationBuilder builds a Recommendation +type RecommendationBuilder struct { + value string + reason string + recoverySuggestion string +} + +// NewRecommendationBuilder creates a new RecommendationBuilder +func NewRecommendationBuilder() *RecommendationBuilder { + return &RecommendationBuilder{} +} + +// WithReason sets the reason of a Recommendation +func (b *RecommendationBuilder) WithReason(reason string) *RecommendationBuilder { + b.reason = reason + return b +} + +// WithValue sets the value of a Recommendation +func (b *RecommendationBuilder) WithValue(value string) *RecommendationBuilder { + b.value = value + return b +} + +// WithRecoverySuggestion sets the recovery suggestion of a Recommendation +func (b *RecommendationBuilder) WithRecoverySuggestion(recoverySuggestion string) *RecommendationBuilder { + b.recoverySuggestion = recoverySuggestion + return b +} + +// Build creates a new Recommendation +func (b *RecommendationBuilder) Build() (*Recommendation, error) { + recommendation := &Recommendation{ + Value: b.value, + Reason: b.reason, + RecoverySuggestion: b.recoverySuggestion, + } + + if len(recommendation.Value) == 0 { + return nil, errors.New("Value cannot be empty") + } + + return recommendation, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation_test.go new file mode 100644 index 0000000..03a7ecf --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/report/recommendation_test.go @@ -0,0 +1,68 @@ +package report + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func TestRecommendationBuilder(t *testing.T) { + recommendation, err := NewRecommendationBuilder(). + WithValue("some_value"). + WithReason("some_reason"). + WithRecoverySuggestion("some_suggestion"). + Build() + + assert.NilError(t, err) + assert.Equal(t, recommendation.Reason, "some_reason") + assert.Equal(t, recommendation.Value, "some_value") + assert.Equal(t, recommendation.RecoverySuggestion, "some_suggestion") +} + +func TestRecommendationBuilder_ShouldRequireValue(t *testing.T) { + _, err := NewRecommendationBuilder().Build() + + assert.Error(t, err, "Value cannot be empty") +} + +func ExampleRecommendationBuilder() { + recommendation, err := NewRecommendationBuilder(). + WithReason("some_reason"). + WithValue("some_value"). + WithRecoverySuggestion("some_suggestion"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(recommendation) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"some_value","reason":"some_reason","recovery_suggestion":"some_suggestion"} +} + +func ExampleRecommendationBuilder_minimal() { + recommendation, err := NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(recommendation) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"some_value"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check.go new file mode 100644 index 0000000..99178ea --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check.go @@ -0,0 +1,37 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" +) + +// StaticLivenessCheckBuilder builds a Static LivenessCheck +type StaticLivenessCheckBuilder struct { + livenessCheckBuilder +} + +// NewStaticLivenessCheckBuilder creates a new StaticLivenessCheckBuilder +func NewStaticLivenessCheckBuilder() *StaticLivenessCheckBuilder { + return &StaticLivenessCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *StaticLivenessCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *StaticLivenessCheckBuilder { + b.livenessCheckBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *StaticLivenessCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *StaticLivenessCheckBuilder { + b.livenessCheckBuilder.withBreakdown(breakdown) + return b +} + +// Build creates a new LivenessCheck +func (b *StaticLivenessCheckBuilder) Build() (*LivenessCheck, error) { + livenessCheck := b.livenessCheckBuilder. + withLivenessType(constants.Static). + build() + + return livenessCheck, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check_test.go new file mode 100644 index 0000000..d15113c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/static_liveness_check_test.go @@ -0,0 +1,45 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" +) + +func ExampleStaticLivenessCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewStaticLivenessCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}},"liveness_type":"STATIC"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check.go new file mode 100644 index 0000000..80ca379 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check.go @@ -0,0 +1,75 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// SupplementaryDocumentTextDataCheck represents a supplementary document text data check +type SupplementaryDocumentTextDataCheck struct { + Result SupplementaryDocumentTextDataCheckResult `json:"result"` + *documentCheck +} + +// SupplementaryDocumentTextDataCheckBuilder builds a SupplementaryDocumentTextDataCheck +type SupplementaryDocumentTextDataCheckBuilder struct { + documentCheckBuilder + documentFields map[string]interface{} +} + +// SupplementaryDocumentTextDataCheckResult represents a document text data check result +type SupplementaryDocumentTextDataCheckResult struct { + checkResult + DocumentFields map[string]interface{} `json:"document_fields,omitempty"` +} + +// NewSupplementaryDocumentTextDataCheckBuilder builds a new SupplementaryDocumentTextDataCheckResult +func NewSupplementaryDocumentTextDataCheckBuilder() *SupplementaryDocumentTextDataCheckBuilder { + return &SupplementaryDocumentTextDataCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *SupplementaryDocumentTextDataCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *SupplementaryDocumentTextDataCheckBuilder { + b.documentCheckBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *SupplementaryDocumentTextDataCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *SupplementaryDocumentTextDataCheckBuilder { + b.documentCheckBuilder.withBreakdown(breakdown) + return b +} + +// WithDocumentFilter adds a document filter to the check +func (b *SupplementaryDocumentTextDataCheckBuilder) WithDocumentFilter(filter *filter.DocumentFilter) *SupplementaryDocumentTextDataCheckBuilder { + b.documentCheckBuilder.withDocumentFilter(filter) + return b +} + +// WithDocumentField adds a document field to the text data check +func (b *SupplementaryDocumentTextDataCheckBuilder) WithDocumentField(key string, value interface{}) *SupplementaryDocumentTextDataCheckBuilder { + if b.documentFields == nil { + b.documentFields = make(map[string]interface{}) + } + b.documentFields[key] = value + return b +} + +// WithDocumentFields sets document fields +func (b *SupplementaryDocumentTextDataCheckBuilder) WithDocumentFields(documentFields map[string]interface{}) *SupplementaryDocumentTextDataCheckBuilder { + b.documentFields = documentFields + return b +} + +// Build creates a new SupplementaryDocumentTextDataCheck +func (b *SupplementaryDocumentTextDataCheckBuilder) Build() (*SupplementaryDocumentTextDataCheck, error) { + docCheck := b.documentCheckBuilder.build() + + return &SupplementaryDocumentTextDataCheck{ + documentCheck: docCheck, + Result: SupplementaryDocumentTextDataCheckResult{ + checkResult: docCheck.Result, + DocumentFields: b.documentFields, + }, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check_test.go new file mode 100644 index 0000000..630ab54 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/supplementary_document_text_data_check_test.go @@ -0,0 +1,98 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleSupplementaryDocumentTextDataCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewSupplementaryDocumentTextDataCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + WithDocumentFilter(docFilter). + WithDocumentField("some-key", "some-value"). + WithDocumentField("some-other-key", map[string]string{ + "some-nested-key": "some-nested-value", + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]},"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}},"document_filter":{"document_types":[],"country_codes":[]}} +} + +func ExampleSupplementaryDocumentTextDataCheckBuilder_WithDocumentFields() { + check, err := NewSupplementaryDocumentTextDataCheckBuilder(). + WithDocumentFields(map[string]interface{}{ + "some-key": "some-value", + "some-other-key": map[string]string{ + "some-nested-key": "some-nested-value", + }, + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{},"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}}} +} + +func ExampleSupplementaryDocumentTextDataCheckBuilder_minimal() { + check, err := NewSupplementaryDocumentTextDataCheckBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{}}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check.go new file mode 100644 index 0000000..d5e01da --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check.go @@ -0,0 +1,37 @@ +package check + +import "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + +// ThirdPartyIdentityCheck defines a sandbox check with a third party credit reporting agency +type ThirdPartyIdentityCheck struct { + *check +} + +// ThirdPartyIdentityCheckBuilder builds a ThirdPartyIdentityCheck +type ThirdPartyIdentityCheckBuilder struct { + checkBuilder +} + +// NewThirdPartyIdentityCheckBuilder creates a new ThirdPartyIdentityCheckBuilder +func NewThirdPartyIdentityCheckBuilder() *ThirdPartyIdentityCheckBuilder { + return &ThirdPartyIdentityCheckBuilder{} +} + +// Build creates a new ThirdPartyIdentityCheck +func (b *ThirdPartyIdentityCheckBuilder) Build() (*ThirdPartyIdentityCheck, error) { + tpiCheck := ThirdPartyIdentityCheck{ + check: b.checkBuilder.build(), + } + + return &tpiCheck, nil +} + +func (b *ThirdPartyIdentityCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *ThirdPartyIdentityCheckBuilder { + b.checkBuilder.withBreakdown(breakdown) + return b +} + +func (b *ThirdPartyIdentityCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *ThirdPartyIdentityCheckBuilder { + b.checkBuilder.withRecommendation(recommendation) + return b +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check_test.go new file mode 100644 index 0000000..97eadc1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/third_party_identity_check_test.go @@ -0,0 +1,45 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" +) + +func ExampleThirdPartyIdentityCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewThirdPartyIdentityCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check.go new file mode 100644 index 0000000..0e25107 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check.go @@ -0,0 +1,37 @@ +package check + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" +) + +// ZoomLivenessCheckBuilder builds a "ZOOM" LivenessCheck +type ZoomLivenessCheckBuilder struct { + livenessCheckBuilder +} + +// NewZoomLivenessCheckBuilder creates a new ZoomLivenessCheckBuilder +func NewZoomLivenessCheckBuilder() *ZoomLivenessCheckBuilder { + return &ZoomLivenessCheckBuilder{} +} + +// WithRecommendation sets the recommendation on the check +func (b *ZoomLivenessCheckBuilder) WithRecommendation(recommendation *report.Recommendation) *ZoomLivenessCheckBuilder { + b.livenessCheckBuilder.withRecommendation(recommendation) + return b +} + +// WithBreakdown adds a breakdown item to the check +func (b *ZoomLivenessCheckBuilder) WithBreakdown(breakdown *report.Breakdown) *ZoomLivenessCheckBuilder { + b.livenessCheckBuilder.withBreakdown(breakdown) + return b +} + +// Build creates a new LivenessCheck +func (b *ZoomLivenessCheckBuilder) Build() (*LivenessCheck, error) { + livenessCheck := b.livenessCheckBuilder. + withLivenessType(constants.Zoom). + build() + + return livenessCheck, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check_test.go new file mode 100644 index 0000000..31da3b2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check/zoom_liveness_check_test.go @@ -0,0 +1,45 @@ +package check + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" +) + +func ExampleZoomLivenessCheckBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + check, err := NewZoomLivenessCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}},"liveness_type":"ZOOM"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports.go new file mode 100644 index 0000000..6446dda --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports.go @@ -0,0 +1,105 @@ +package request + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check" +) + +// CheckReports represents check reports +type CheckReports struct { + DocumentAuthenticityChecks []*check.DocumentAuthenticityCheck `json:"ID_DOCUMENT_AUTHENTICITY"` + DocumentTextDataChecks []*check.DocumentTextDataCheck `json:"ID_DOCUMENT_TEXT_DATA_CHECK"` + DocumentFaceMatchChecks []*check.DocumentFaceMatchCheck `json:"ID_DOCUMENT_FACE_MATCH"` + LivenessChecks []*check.LivenessCheck `json:"LIVENESS"` + IDDocumentComparisonChecks []*check.IDDocumentComparisonCheck `json:"ID_DOCUMENT_COMPARISON"` + SupplementaryDocumentTextDataChecks []*check.SupplementaryDocumentTextDataCheck `json:"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK"` + AsyncReportDelay uint32 `json:"async_report_delay,omitempty"` + ThirdPartyIdentityCheck *check.ThirdPartyIdentityCheck `json:"THIRD_PARTY_IDENTITY,omitempty"` +} + +// CheckReportsBuilder builds CheckReports +type CheckReportsBuilder struct { + documentAuthenticityChecks []*check.DocumentAuthenticityCheck + documentTextDataChecks []*check.DocumentTextDataCheck + documentFaceMatchChecks []*check.DocumentFaceMatchCheck + livenessChecks []*check.LivenessCheck + idDocumentComparisonChecks []*check.IDDocumentComparisonCheck + supplementaryDocumentTextDataChecks []*check.SupplementaryDocumentTextDataCheck + thirdPartyIdentityCheck *check.ThirdPartyIdentityCheck + asyncReportDelay uint32 +} + +// NewCheckReportsBuilder creates a new CheckReportsBuilder +func NewCheckReportsBuilder() *CheckReportsBuilder { + return &CheckReportsBuilder{ + documentAuthenticityChecks: []*check.DocumentAuthenticityCheck{}, + documentTextDataChecks: []*check.DocumentTextDataCheck{}, + documentFaceMatchChecks: []*check.DocumentFaceMatchCheck{}, + livenessChecks: []*check.LivenessCheck{}, + idDocumentComparisonChecks: []*check.IDDocumentComparisonCheck{}, + supplementaryDocumentTextDataChecks: []*check.SupplementaryDocumentTextDataCheck{}, + } +} + +// WithDocumentAuthenticityCheck adds a document authenticity check +func (b *CheckReportsBuilder) WithDocumentAuthenticityCheck(documentAuthenticityCheck *check.DocumentAuthenticityCheck) *CheckReportsBuilder { + b.documentAuthenticityChecks = append(b.documentAuthenticityChecks, documentAuthenticityCheck) + return b +} + +// WithDocumentTextDataCheck adds a document text data check +func (b *CheckReportsBuilder) WithDocumentTextDataCheck(documentTextDataCheck *check.DocumentTextDataCheck) *CheckReportsBuilder { + b.documentTextDataChecks = append(b.documentTextDataChecks, documentTextDataCheck) + return b +} + +// WithDocumentTextDataCheck adds a supplementary document text data check +func (b *CheckReportsBuilder) WithSupplementaryDocumentTextDataCheck(supplementaryDocumentTextDataCheck *check.SupplementaryDocumentTextDataCheck) *CheckReportsBuilder { + b.supplementaryDocumentTextDataChecks = append( + b.supplementaryDocumentTextDataChecks, + supplementaryDocumentTextDataCheck, + ) + return b +} + +// WithDocumentFaceMatchCheck adds a document face match check +func (b *CheckReportsBuilder) WithDocumentFaceMatchCheck(documentFaceMatchCheck *check.DocumentFaceMatchCheck) *CheckReportsBuilder { + b.documentFaceMatchChecks = append(b.documentFaceMatchChecks, documentFaceMatchCheck) + return b +} + +// WithLivenessCheck adds a liveness check +func (b *CheckReportsBuilder) WithLivenessCheck(livenessCheck *check.LivenessCheck) *CheckReportsBuilder { + b.livenessChecks = append(b.livenessChecks, livenessCheck) + return b +} + +// WithIDDocumentComparisonCheck adds an identity document comparison check +func (b *CheckReportsBuilder) WithIDDocumentComparisonCheck(idDocumentComparisonCheck *check.IDDocumentComparisonCheck) *CheckReportsBuilder { + b.idDocumentComparisonChecks = append(b.idDocumentComparisonChecks, idDocumentComparisonCheck) + return b +} + +// WithAsyncReportDelay sets the async report delay +func (b *CheckReportsBuilder) WithAsyncReportDelay(asyncReportDelay uint32) *CheckReportsBuilder { + b.asyncReportDelay = asyncReportDelay + return b +} + +func (b *CheckReportsBuilder) WithThirdPartyIdentityCheck(thirdPartyIdentityCheck *check.ThirdPartyIdentityCheck) *CheckReportsBuilder { + b.thirdPartyIdentityCheck = thirdPartyIdentityCheck + return b +} + +// Build creates CheckReports +func (b *CheckReportsBuilder) Build() (CheckReports, error) { + return CheckReports{ + DocumentAuthenticityChecks: b.documentAuthenticityChecks, + DocumentTextDataChecks: b.documentTextDataChecks, + DocumentFaceMatchChecks: b.documentFaceMatchChecks, + LivenessChecks: b.livenessChecks, + IDDocumentComparisonChecks: b.idDocumentComparisonChecks, + SupplementaryDocumentTextDataChecks: b.supplementaryDocumentTextDataChecks, + AsyncReportDelay: b.asyncReportDelay, + ThirdPartyIdentityCheck: b.thirdPartyIdentityCheck, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports_test.go new file mode 100644 index 0000000..9f0b576 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/check_reports_test.go @@ -0,0 +1,130 @@ +package request + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/check/report" + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleCheckReportsBuilder() { + breakdown, err := report.NewBreakdownBuilder(). + WithResult("some_result"). + WithSubCheck("some_check"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := report.NewRecommendationBuilder(). + WithValue("some_value"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + authenticityCheck, err := check.NewDocumentAuthenticityCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + faceMatchCheck, err := check.NewDocumentFaceMatchCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + textDataCheck, err := check.NewDocumentTextDataCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + supplementaryDocumentTextDataCheck, err := check.NewSupplementaryDocumentTextDataCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + thirdPartyCheck, err := check.NewThirdPartyIdentityCheckBuilder(). + WithBreakdown(breakdown). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + zoomLivenessCheck, err := check.NewZoomLivenessCheckBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + documentFiler, err := filter.NewDocumentFilterBuilder().Build() + idDocumentComparisonCheck, err := check.NewIDDocumentComparisonCheckBuilder(). + WithSecondaryDocumentFilter(documentFiler). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + checkReports, err := NewCheckReportsBuilder(). + WithDocumentAuthenticityCheck(authenticityCheck). + WithDocumentFaceMatchCheck(faceMatchCheck). + WithDocumentTextDataCheck(textDataCheck). + WithLivenessCheck(zoomLivenessCheck). + WithIDDocumentComparisonCheck(idDocumentComparisonCheck). + WithSupplementaryDocumentTextDataCheck(supplementaryDocumentTextDataCheck). + WithThirdPartyIdentityCheck(thirdPartyCheck). + WithAsyncReportDelay(10). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(checkReports) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"ID_DOCUMENT_AUTHENTICITY":[{"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}}}],"ID_DOCUMENT_TEXT_DATA_CHECK":[{"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}}}],"ID_DOCUMENT_FACE_MATCH":[{"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}}}],"LIVENESS":[{"result":{"report":{}},"liveness_type":"ZOOM"}],"ID_DOCUMENT_COMPARISON":[{"result":{"report":{}},"secondary_document_filter":{"document_types":[],"country_codes":[]}}],"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK":[{"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}}}],"async_report_delay":10,"THIRD_PARTY_IDENTITY":{"result":{"report":{"recommendation":{"value":"some_value"},"breakdown":[{"sub_check":"some_check","result":"some_result","details":[]}]}}}} +} + +func ExampleCheckReportsBuilder_minimal() { + checkReports, err := NewCheckReportsBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(checkReports) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"ID_DOCUMENT_AUTHENTICITY":[],"ID_DOCUMENT_TEXT_DATA_CHECK":[],"ID_DOCUMENT_FACE_MATCH":[],"LIVENESS":[],"ID_DOCUMENT_COMPARISON":[],"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK":[]} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter.go new file mode 100644 index 0000000..e7fe86e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter.go @@ -0,0 +1,53 @@ +package filter + +// DocumentFilter represents a document filter for checks and tasks +type DocumentFilter struct { + DocumentTypes []string `json:"document_types"` + CountryCodes []string `json:"country_codes"` +} + +// DocumentFilterBuilder builds a DocumentFilter +type DocumentFilterBuilder struct { + documentTypes []string + countryCodes []string +} + +// NewDocumentFilterBuilder creates a new DocumentFilterBuilder +func NewDocumentFilterBuilder() *DocumentFilterBuilder { + return &DocumentFilterBuilder{ + documentTypes: []string{}, + countryCodes: []string{}, + } +} + +// WithCountryCode adds a country code to the filter +func (b *DocumentFilterBuilder) WithCountryCode(countryCode string) *DocumentFilterBuilder { + b.countryCodes = append(b.countryCodes, countryCode) + return b +} + +// WithCountryCodes sets the country codes of the filter +func (b *DocumentFilterBuilder) WithCountryCodes(countryCodes []string) *DocumentFilterBuilder { + b.countryCodes = countryCodes + return b +} + +// WithDocumentType adds a document type to the filter +func (b *DocumentFilterBuilder) WithDocumentType(documentType string) *DocumentFilterBuilder { + b.documentTypes = append(b.documentTypes, documentType) + return b +} + +// WithDocumentTypes sets the document types of the filter +func (b *DocumentFilterBuilder) WithDocumentTypes(documentTypes []string) *DocumentFilterBuilder { + b.documentTypes = documentTypes + return b +} + +// Build creates a new DocumentFilter +func (b *DocumentFilterBuilder) Build() (*DocumentFilter, error) { + return &DocumentFilter{ + DocumentTypes: b.documentTypes, + CountryCodes: b.countryCodes, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter_test.go new file mode 100644 index 0000000..be80e85 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/filter/document_filter_test.go @@ -0,0 +1,92 @@ +package filter + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func TestDocumentFilterBuilder_WithCountryCode(t *testing.T) { + filter, err := NewDocumentFilterBuilder(). + WithDocumentType("some_type"). + Build() + + assert.NilError(t, err) + assert.Equal(t, filter.DocumentTypes[0], "some_type") +} + +func TestDocumentFilterBuilder_WithDocumentType(t *testing.T) { + filter, err := NewDocumentFilterBuilder(). + WithCountryCode("some_country"). + Build() + + assert.NilError(t, err) + assert.Equal(t, filter.CountryCodes[0], "some_country") +} + +func ExampleDocumentFilterBuilder() { + filter, err := NewDocumentFilterBuilder(). + WithCountryCode("some_country"). + WithDocumentType("some_type"). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(filter) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_types":["some_type"],"country_codes":["some_country"]} +} + +func ExampleDocumentFilterBuilder_multipleCountriesAndDocumentTypes() { + filter, err := NewDocumentFilterBuilder(). + WithCountryCode("some_country"). + WithCountryCode("some_other_country"). + WithDocumentType("some_type"). + WithDocumentType("some_other_type"). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(filter) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_types":["some_type","some_other_type"],"country_codes":["some_country","some_other_country"]} +} + +func ExampleDocumentFilterBuilder_countriesAndDocumentTypes() { + filter, err := NewDocumentFilterBuilder(). + WithCountryCodes([]string{"some_country", "some_other_country"}). + WithDocumentTypes([]string{"some_type", "some_other_type"}). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(filter) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_types":["some_type","some_other_type"],"country_codes":["some_country","some_other_country"]} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config.go new file mode 100644 index 0000000..a8cd28b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config.go @@ -0,0 +1,48 @@ +package request + +import ( + "errors" +) + +// ResponseConfig represents the response config +type ResponseConfig struct { + TaskResults *TaskResults `json:"task_results,omitempty"` + CheckReports *CheckReports `json:"check_reports"` +} + +// ResponseConfigBuilder builds ResponseConfig +type ResponseConfigBuilder struct { + taskResults *TaskResults + checkReports *CheckReports +} + +// NewResponseConfigBuilder creates a new ResponseConfigBuilder +func NewResponseConfigBuilder() *ResponseConfigBuilder { + return &ResponseConfigBuilder{} +} + +// WithTaskResults adds task results to the response configuration +func (b *ResponseConfigBuilder) WithTaskResults(taskResults TaskResults) *ResponseConfigBuilder { + b.taskResults = &taskResults + return b +} + +// WithCheckReports adds check reports to the response configuration +func (b *ResponseConfigBuilder) WithCheckReports(checkReports CheckReports) *ResponseConfigBuilder { + b.checkReports = &checkReports + return b +} + +// Build creates ResponseConfig +func (b *ResponseConfigBuilder) Build() (*ResponseConfig, error) { + responseConfig := &ResponseConfig{ + CheckReports: b.checkReports, + TaskResults: b.taskResults, + } + + if responseConfig.CheckReports == nil { + return nil, errors.New("Check Reports must be provided") + } + + return responseConfig, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config_test.go new file mode 100644 index 0000000..0c1b735 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/response_config_test.go @@ -0,0 +1,72 @@ +package request + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func TestResponseConfigBuilder_Build_ShouldRequireCheckReports(t *testing.T) { + _, err := NewResponseConfigBuilder().Build() + + assert.Error(t, err, "Check Reports must be provided") +} + +func ExampleResponseConfigBuilder() { + taskResults, err := NewTaskResultsBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + checkReports, err := NewCheckReportsBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + responseConfig, err := NewResponseConfigBuilder(). + WithTaskResults(taskResults). + WithCheckReports(checkReports). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(responseConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"task_results":{"ID_DOCUMENT_TEXT_DATA_EXTRACTION":[],"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION":[]},"check_reports":{"ID_DOCUMENT_AUTHENTICITY":[],"ID_DOCUMENT_TEXT_DATA_CHECK":[],"ID_DOCUMENT_FACE_MATCH":[],"LIVENESS":[],"ID_DOCUMENT_COMPARISON":[],"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK":[]}} +} + +func ExampleResponseConfigBuilder_minimal() { + checkReports, err := NewCheckReportsBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + responseConfig, err := NewResponseConfigBuilder(). + WithCheckReports(checkReports). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(responseConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"check_reports":{"ID_DOCUMENT_AUTHENTICITY":[],"ID_DOCUMENT_TEXT_DATA_CHECK":[],"ID_DOCUMENT_FACE_MATCH":[],"LIVENESS":[],"ID_DOCUMENT_COMPARISON":[],"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_CHECK":[]}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_task.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_task.go new file mode 100644 index 0000000..6c4e5b6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_task.go @@ -0,0 +1,23 @@ +package task + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +type documentTask struct { + DocumentFilter *filter.DocumentFilter `json:"document_filter,omitempty"` +} + +type documentTaskBuilder struct { + documentFilter *filter.DocumentFilter +} + +func (b *documentTaskBuilder) withDocumentFilter(filter *filter.DocumentFilter) { + b.documentFilter = filter +} + +func (b *documentTaskBuilder) build() *documentTask { + return &documentTask{ + DocumentFilter: b.documentFilter, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task.go new file mode 100644 index 0000000..a3dd317 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task.go @@ -0,0 +1,94 @@ +package task + +import ( + "encoding/base64" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// DocumentTextDataExtractionTask represents a document text data extraction task +type DocumentTextDataExtractionTask struct { + *documentTask + Result documentTextDataExtractionTaskResult `json:"result"` +} + +// DocumentTextDataExtractionTaskBuilder builds a DocumentTextDataExtractionTask +type DocumentTextDataExtractionTaskBuilder struct { + documentTaskBuilder + documentFields map[string]interface{} + documentIDPhoto *documentIDPhoto + detectedCountry string + recommendation *TextDataExtractionRecommendation +} + +type documentTextDataExtractionTaskResult struct { + DocumentFields map[string]interface{} `json:"document_fields,omitempty"` + DocumentIDPhoto *documentIDPhoto `json:"document_id_photo,omitempty"` + DetectedCountry string `json:"detected_country,omitempty"` + Recommendation *TextDataExtractionRecommendation `json:"recommendation,omitempty"` +} + +type documentIDPhoto struct { + ContentType string `json:"content_type"` + Data string `json:"data"` +} + +// NewDocumentTextDataExtractionTaskBuilder creates a new DocumentTextDataExtractionTaskBuilder +func NewDocumentTextDataExtractionTaskBuilder() *DocumentTextDataExtractionTaskBuilder { + return &DocumentTextDataExtractionTaskBuilder{} +} + +// WithDocumentFilter adds a document filter to the task +func (b *DocumentTextDataExtractionTaskBuilder) WithDocumentFilter(filter *filter.DocumentFilter) *DocumentTextDataExtractionTaskBuilder { + b.documentTaskBuilder.withDocumentFilter(filter) + return b +} + +// WithDocumentField adds a document field to the task +func (b *DocumentTextDataExtractionTaskBuilder) WithDocumentField(key string, value interface{}) *DocumentTextDataExtractionTaskBuilder { + if b.documentFields == nil { + b.documentFields = make(map[string]interface{}) + } + b.documentFields[key] = value + return b +} + +// WithDocumentFields sets document fields +func (b *DocumentTextDataExtractionTaskBuilder) WithDocumentFields(documentFields map[string]interface{}) *DocumentTextDataExtractionTaskBuilder { + b.documentFields = documentFields + return b +} + +// WithDocumentIDPhoto sets the document ID photo +func (b *DocumentTextDataExtractionTaskBuilder) WithDocumentIDPhoto(contentType string, data []byte) *DocumentTextDataExtractionTaskBuilder { + b.documentIDPhoto = &documentIDPhoto{ + ContentType: contentType, + Data: base64.StdEncoding.EncodeToString(data), + } + return b +} + +// WithDetectedCountry sets the detected country +func (b *DocumentTextDataExtractionTaskBuilder) WithDetectedCountry(detectedCountry string) *DocumentTextDataExtractionTaskBuilder { + b.detectedCountry = detectedCountry + return b +} + +// WithRecommendation sets the recommendation +func (b *DocumentTextDataExtractionTaskBuilder) WithRecommendation(recommendation *TextDataExtractionRecommendation) *DocumentTextDataExtractionTaskBuilder { + b.recommendation = recommendation + return b +} + +// Build creates a new DocumentTextDataExtractionTask +func (b *DocumentTextDataExtractionTaskBuilder) Build() (*DocumentTextDataExtractionTask, error) { + return &DocumentTextDataExtractionTask{ + documentTask: b.documentTaskBuilder.build(), + Result: documentTextDataExtractionTaskResult{ + DocumentFields: b.documentFields, + DocumentIDPhoto: b.documentIDPhoto, + DetectedCountry: b.detectedCountry, + Recommendation: b.recommendation, + }, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task_test.go new file mode 100644 index 0000000..afc03bc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/document_text_data_extraction_task_test.go @@ -0,0 +1,150 @@ +package task + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleDocumentTextDataExtractionTaskBuilder() { + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + task, err := NewDocumentTextDataExtractionTaskBuilder(). + WithDocumentFilter(docFilter). + WithDocumentField("some-key", "some-value"). + WithDocumentField("some-other-key", map[string]string{ + "some-nested-key": "some-nested-value", + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_filter":{"document_types":[],"country_codes":[]},"result":{"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}}} +} + +func ExampleDocumentTextDataExtractionTaskBuilder_WithDocumentFields() { + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + task, err := NewDocumentTextDataExtractionTaskBuilder(). + WithDocumentFilter(docFilter). + WithDocumentFields(map[string]interface{}{ + "some-key": "some-value", + "some-other-key": map[string]string{ + "some-nested-key": "some-nested-value", + }, + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_filter":{"document_types":[],"country_codes":[]},"result":{"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}}} +} + +func ExampleDocumentTextDataExtractionTaskBuilder_WithDocumentIDPhoto() { + task, err := NewDocumentTextDataExtractionTaskBuilder(). + WithDocumentIDPhoto("some-content-type", []byte{0xDE, 0xAD, 0xBE, 0xEF}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"document_id_photo":{"content_type":"some-content-type","data":"3q2+7w=="}}} +} + +func ExampleDocumentTextDataExtractionTaskBuilder_WithDetectedCountry() { + task, err := NewDocumentTextDataExtractionTaskBuilder(). + WithDetectedCountry("some-country"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"detected_country":"some-country"}} +} + +func ExampleDocumentTextDataExtractionTaskBuilder_WithRecommendation() { + recommendation, err := NewTextDataExtractionRecommendationBuilder(). + ForProgress(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + task, err := NewDocumentTextDataExtractionTaskBuilder(). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"recommendation":{"value":"PROGRESS"}}} +} + +func ExampleDocumentTextDataExtractionTaskBuilder_minimal() { + task, err := NewDocumentTextDataExtractionTaskBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task.go new file mode 100644 index 0000000..38d4e63 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task.go @@ -0,0 +1,75 @@ +package task + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +// SupplementaryDocumentTextDataExtractionTask represents a document text data extraction task +type SupplementaryDocumentTextDataExtractionTask struct { + *documentTask + Result supplementaryDocumentTextDataExtractionTaskResult `json:"result"` +} + +// SupplementaryDocumentTextDataExtractionTaskBuilder builds a SupplementaryDocumentTextDataExtractionTask +type SupplementaryDocumentTextDataExtractionTaskBuilder struct { + documentTaskBuilder + documentFields map[string]interface{} + detectedCountry string + recommendation *TextDataExtractionRecommendation +} + +type supplementaryDocumentTextDataExtractionTaskResult struct { + DocumentFields map[string]interface{} `json:"document_fields,omitempty"` + DetectedCountry string `json:"detected_country,omitempty"` + Recommendation *TextDataExtractionRecommendation `json:"recommendation,omitempty"` +} + +// NewSupplementaryDocumentTextDataExtractionTaskBuilder creates a new SupplementaryDocumentTextDataExtractionTaskBuilder +func NewSupplementaryDocumentTextDataExtractionTaskBuilder() *SupplementaryDocumentTextDataExtractionTaskBuilder { + return &SupplementaryDocumentTextDataExtractionTaskBuilder{} +} + +// WithDocumentFilter adds a document filter to the task +func (b *SupplementaryDocumentTextDataExtractionTaskBuilder) WithDocumentFilter(filter *filter.DocumentFilter) *SupplementaryDocumentTextDataExtractionTaskBuilder { + b.documentTaskBuilder.withDocumentFilter(filter) + return b +} + +// WithDocumentField adds a document field to the task +func (b *SupplementaryDocumentTextDataExtractionTaskBuilder) WithDocumentField(key string, value interface{}) *SupplementaryDocumentTextDataExtractionTaskBuilder { + if b.documentFields == nil { + b.documentFields = make(map[string]interface{}) + } + b.documentFields[key] = value + return b +} + +// WithDocumentFields sets document fields +func (b *SupplementaryDocumentTextDataExtractionTaskBuilder) WithDocumentFields(documentFields map[string]interface{}) *SupplementaryDocumentTextDataExtractionTaskBuilder { + b.documentFields = documentFields + return b +} + +// WithDetectedCountry sets the detected country +func (b *SupplementaryDocumentTextDataExtractionTaskBuilder) WithDetectedCountry(detectedCountry string) *SupplementaryDocumentTextDataExtractionTaskBuilder { + b.detectedCountry = detectedCountry + return b +} + +// WithRecommendation sets the recommendation +func (b *SupplementaryDocumentTextDataExtractionTaskBuilder) WithRecommendation(recommendation *TextDataExtractionRecommendation) *SupplementaryDocumentTextDataExtractionTaskBuilder { + b.recommendation = recommendation + return b +} + +// Build creates a new SupplementaryDocumentTextDataExtractionTask +func (b *SupplementaryDocumentTextDataExtractionTaskBuilder) Build() (*SupplementaryDocumentTextDataExtractionTask, error) { + return &SupplementaryDocumentTextDataExtractionTask{ + documentTask: b.documentTaskBuilder.build(), + Result: supplementaryDocumentTextDataExtractionTaskResult{ + DocumentFields: b.documentFields, + DetectedCountry: b.detectedCountry, + Recommendation: b.recommendation, + }, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task_test.go new file mode 100644 index 0000000..31c73b0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/supplementary_document_text_data_extraction_task_test.go @@ -0,0 +1,131 @@ +package task + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/filter" +) + +func ExampleSupplementaryDocumentTextDataExtractionTaskBuilder() { + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + task, err := NewSupplementaryDocumentTextDataExtractionTaskBuilder(). + WithDocumentFilter(docFilter). + WithDocumentField("some-key", "some-value"). + WithDocumentField("some-other-key", map[string]string{ + "some-nested-key": "some-nested-value", + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_filter":{"document_types":[],"country_codes":[]},"result":{"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}}} +} + +func ExampleSupplementaryDocumentTextDataExtractionTaskBuilder_WithDocumentFields() { + docFilter, err := filter.NewDocumentFilterBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + task, err := NewSupplementaryDocumentTextDataExtractionTaskBuilder(). + WithDocumentFilter(docFilter). + WithDocumentFields(map[string]interface{}{ + "some-key": "some-value", + "some-other-key": map[string]string{ + "some-nested-key": "some-nested-value", + }, + }). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_filter":{"document_types":[],"country_codes":[]},"result":{"document_fields":{"some-key":"some-value","some-other-key":{"some-nested-key":"some-nested-value"}}}} +} + +func ExampleSupplementaryDocumentTextDataExtractionTaskBuilder_WithDetectedCountry() { + task, err := NewSupplementaryDocumentTextDataExtractionTaskBuilder(). + WithDetectedCountry("some-country"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"detected_country":"some-country"}} +} + +func ExampleSupplementaryDocumentTextDataExtractionTaskBuilder_WithRecommendation() { + recommendation, err := NewTextDataExtractionRecommendationBuilder(). + ForProgress(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + task, err := NewSupplementaryDocumentTextDataExtractionTaskBuilder(). + WithRecommendation(recommendation). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{"recommendation":{"value":"PROGRESS"}}} +} + +func ExampleSupplementaryDocumentTextDataExtractionTaskBuilder_minimal() { + task, err := NewSupplementaryDocumentTextDataExtractionTaskBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"result":{}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason.go new file mode 100644 index 0000000..9b6b180 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason.go @@ -0,0 +1,49 @@ +package task + +const ( + valueQuality string = "QUALITY" + valueUserError string = "USER_ERROR" +) + +// TextDataExtractionReason represents a text data extraction reason +type TextDataExtractionReason struct { + Value string `json:"value"` + Detail string `json:"detail,omitempty"` +} + +// TextDataExtractionReasonBuilder builds a TextDataExtractionReason +type TextDataExtractionReasonBuilder struct { + value string + detail string +} + +// NewTextDataExtractionReasonBuilder creates a new TextDataExtractionReasonBuilder +func NewTextDataExtractionReasonBuilder() *TextDataExtractionReasonBuilder { + return &TextDataExtractionReasonBuilder{} +} + +// ForQuality sets the reason to quality +func (b *TextDataExtractionReasonBuilder) ForQuality() *TextDataExtractionReasonBuilder { + b.value = valueQuality + return b +} + +// ForUserError sets the reason to user error +func (b *TextDataExtractionReasonBuilder) ForUserError() *TextDataExtractionReasonBuilder { + b.value = valueUserError + return b +} + +// WithDetail sets the reason detail +func (b *TextDataExtractionReasonBuilder) WithDetail(detail string) *TextDataExtractionReasonBuilder { + b.detail = detail + return b +} + +// Build creates a new TextDataExtractionReason +func (b *TextDataExtractionReasonBuilder) Build() (*TextDataExtractionReason, error) { + return &TextDataExtractionReason{ + Detail: b.detail, + Value: b.value, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason_test.go new file mode 100644 index 0000000..ac64ed4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_reason_test.go @@ -0,0 +1,63 @@ +package task + +import ( + "encoding/json" + "fmt" +) + +func ExampleTextDataExtractionReasonBuilder() { + reason, err := NewTextDataExtractionReasonBuilder(). + ForQuality(). + WithDetail("some-detail"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(reason) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"QUALITY","detail":"some-detail"} +} + +func ExampleTextDataExtractionReasonBuilder_ForQuality() { + reason, err := NewTextDataExtractionReasonBuilder(). + ForQuality(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(reason) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"QUALITY"} +} +func ExampleTextDataExtractionReasonBuilder_ForUserError() { + reason, err := NewTextDataExtractionReasonBuilder(). + ForUserError(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(reason) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"USER_ERROR"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation.go new file mode 100644 index 0000000..4daf35c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation.go @@ -0,0 +1,56 @@ +package task + +const ( + valueProgress string = "PROGRESS" + valueMustTryAgain string = "MUST_TRY_AGAIN" + valueShouldTryAgain string = "SHOULD_TRY_AGAIN" +) + +// TextDataExtractionRecommendation represents a text data extraction reason +type TextDataExtractionRecommendation struct { + Value string `json:"value"` + Reason *TextDataExtractionReason `json:"reason,omitempty"` +} + +// TextDataExtractionRecommendationBuilder builds a TextDataExtractionRecommendation +type TextDataExtractionRecommendationBuilder struct { + value string + reason *TextDataExtractionReason +} + +// NewTextDataExtractionRecommendationBuilder creates a new TextDataExtractionRecommendationBuilder +func NewTextDataExtractionRecommendationBuilder() *TextDataExtractionRecommendationBuilder { + return &TextDataExtractionRecommendationBuilder{} +} + +// ForProgress sets the recommendation value to progress +func (b *TextDataExtractionRecommendationBuilder) ForProgress() *TextDataExtractionRecommendationBuilder { + b.value = valueProgress + return b +} + +// ForMustTryAgain sets the recommendation value to must try again +func (b *TextDataExtractionRecommendationBuilder) ForMustTryAgain() *TextDataExtractionRecommendationBuilder { + b.value = valueMustTryAgain + return b +} + +// ForShouldTryAgain sets the recommendation value to should try again +func (b *TextDataExtractionRecommendationBuilder) ForShouldTryAgain() *TextDataExtractionRecommendationBuilder { + b.value = valueShouldTryAgain + return b +} + +// WithReason sets the recommendation reason +func (b *TextDataExtractionRecommendationBuilder) WithReason(reason *TextDataExtractionReason) *TextDataExtractionRecommendationBuilder { + b.reason = reason + return b +} + +// Build creates a new TextDataExtractionRecommendation +func (b *TextDataExtractionRecommendationBuilder) Build() (*TextDataExtractionRecommendation, error) { + return &TextDataExtractionRecommendation{ + Value: b.value, + Reason: b.reason, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation_test.go new file mode 100644 index 0000000..5c4dcb1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task/text_data_extraction_recommendation_test.go @@ -0,0 +1,91 @@ +package task + +import ( + "encoding/json" + "fmt" +) + +func ExampleTextDataExtractionRecommendationBuilder() { + reason, err := NewTextDataExtractionReasonBuilder(). + ForQuality(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + recommendation, err := NewTextDataExtractionRecommendationBuilder(). + ForShouldTryAgain(). + WithReason(reason). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(recommendation) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"SHOULD_TRY_AGAIN","reason":{"value":"QUALITY"}} +} + +func ExampleTextDataExtractionRecommendationBuilder_ForProgress() { + recommendation, err := NewTextDataExtractionRecommendationBuilder(). + ForProgress(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(recommendation) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"PROGRESS"} +} + +func ExampleTextDataExtractionRecommendationBuilder_ForShouldTryAgain() { + recommendation, err := NewTextDataExtractionRecommendationBuilder(). + ForShouldTryAgain(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(recommendation) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"SHOULD_TRY_AGAIN"} +} + +func ExampleTextDataExtractionRecommendationBuilder_ForMustTryAgain() { + recommendation, err := NewTextDataExtractionRecommendationBuilder(). + ForMustTryAgain(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(recommendation) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"value":"MUST_TRY_AGAIN"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results.go new file mode 100644 index 0000000..43dc4ca --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results.go @@ -0,0 +1,45 @@ +package request + +import ( + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/task" +) + +// TaskResults represents task results +type TaskResults struct { + DocumentTextDataExtractionTasks []*task.DocumentTextDataExtractionTask `json:"ID_DOCUMENT_TEXT_DATA_EXTRACTION"` + SupplementaryDocumentTextDataExtractionTasks []*task.SupplementaryDocumentTextDataExtractionTask `json:"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION"` +} + +// TaskResultsBuilder builds TaskResults +type TaskResultsBuilder struct { + documentTextDataExtractionTasks []*task.DocumentTextDataExtractionTask + supplementaryDocumentTextDataExtractionTasks []*task.SupplementaryDocumentTextDataExtractionTask +} + +// NewTaskResultsBuilder creates a new TaskResultsBuilder +func NewTaskResultsBuilder() *TaskResultsBuilder { + return &TaskResultsBuilder{ + documentTextDataExtractionTasks: []*task.DocumentTextDataExtractionTask{}, + supplementaryDocumentTextDataExtractionTasks: []*task.SupplementaryDocumentTextDataExtractionTask{}, + } +} + +// WithDocumentTextDataExtractionTask adds a supplementary document text data extraction task +func (b *TaskResultsBuilder) WithDocumentTextDataExtractionTask(documentTextDataExtractionTask *task.DocumentTextDataExtractionTask) *TaskResultsBuilder { + b.documentTextDataExtractionTasks = append(b.documentTextDataExtractionTasks, documentTextDataExtractionTask) + return b +} + +// WithSupplementaryDocumentTextDataExtractionTask adds a supplementary document text data extraction task +func (b *TaskResultsBuilder) WithSupplementaryDocumentTextDataExtractionTask(supplementaryTextDataExtractionTask *task.SupplementaryDocumentTextDataExtractionTask) *TaskResultsBuilder { + b.supplementaryDocumentTextDataExtractionTasks = append(b.supplementaryDocumentTextDataExtractionTasks, supplementaryTextDataExtractionTask) + return b +} + +// Build creates TaskResults +func (b *TaskResultsBuilder) Build() (TaskResults, error) { + return TaskResults{ + DocumentTextDataExtractionTasks: b.documentTextDataExtractionTasks, + SupplementaryDocumentTextDataExtractionTasks: b.supplementaryDocumentTextDataExtractionTasks, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results_test.go new file mode 100644 index 0000000..c8b89b8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/sandbox/request/task_results_test.go @@ -0,0 +1,42 @@ +package request + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/sandbox/request/task" +) + +func ExampleTaskResultsBuilder() { + textDataExtractionTask, err := task.NewDocumentTextDataExtractionTaskBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + supplementaryTextDataExtractionTask, err := task.NewSupplementaryDocumentTextDataExtractionTaskBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + taskResults, err := NewTaskResultsBuilder(). + WithDocumentTextDataExtractionTask(textDataExtractionTask). + WithSupplementaryDocumentTextDataExtractionTask(supplementaryTextDataExtractionTask). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(taskResults) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"ID_DOCUMENT_TEXT_DATA_EXTRACTION":[{"result":{}}],"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION":[{"result":{}}]} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/constants.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/constants.go new file mode 100644 index 0000000..4b40973 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/constants.go @@ -0,0 +1,6 @@ +package check + +const ( + zoom = "ZOOM" + static = "STATIC" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity.go new file mode 100644 index 0000000..8cd36ed --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity.go @@ -0,0 +1,75 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedDocumentAuthenticityCheck requests creation of a Document Authenticity Check +type RequestedDocumentAuthenticityCheck struct { + config RequestedDocumentAuthenticityConfig +} + +// Type is the type of the Requested Check +func (c *RequestedDocumentAuthenticityCheck) Type() string { + return constants.IDDocumentAuthenticity +} + +// Config is the configuration of the Requested Check +func (c *RequestedDocumentAuthenticityCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig( + c.config, + ) +} + +// MarshalJSON returns the JSON encoding +func (c *RequestedDocumentAuthenticityCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +// RequestedDocumentAuthenticityConfig is the configuration applied when creating a Document Authenticity Check +type RequestedDocumentAuthenticityConfig struct { + ManualCheck string `json:"manual_check,omitempty"` +} + +// RequestedDocumentAuthenticityCheckBuilder builds a RequestedDocumentAuthenticityCheck +type RequestedDocumentAuthenticityCheckBuilder struct { + config RequestedDocumentAuthenticityConfig +} + +// NewRequestedDocumentAuthenticityCheckBuilder creates a new DocumentAuthenticityCheckBuilder +func NewRequestedDocumentAuthenticityCheckBuilder() *RequestedDocumentAuthenticityCheckBuilder { + return &RequestedDocumentAuthenticityCheckBuilder{} +} + +// WithManualCheckAlways requires that a manual follow-up check is always performed +func (b *RequestedDocumentAuthenticityCheckBuilder) WithManualCheckAlways() *RequestedDocumentAuthenticityCheckBuilder { + b.config.ManualCheck = constants.Always + return b +} + +// WithManualCheckFallback requires that a manual follow-up check is performed only on failed Checks, and those with a low level of confidence +func (b *RequestedDocumentAuthenticityCheckBuilder) WithManualCheckFallback() *RequestedDocumentAuthenticityCheckBuilder { + b.config.ManualCheck = constants.Fallback + return b +} + +// WithManualCheckNever requires that only an automated Check is performed. No manual follow-up Check will ever be initiated +func (b *RequestedDocumentAuthenticityCheckBuilder) WithManualCheckNever() *RequestedDocumentAuthenticityCheckBuilder { + b.config.ManualCheck = constants.Never + return b +} + +// Build builds the RequestedDocumentAuthenticityCheck +func (b *RequestedDocumentAuthenticityCheckBuilder) Build() (*RequestedDocumentAuthenticityCheck, error) { + return &RequestedDocumentAuthenticityCheck{ + config: b.config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity_test.go new file mode 100644 index 0000000..36afa71 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_authenticity_test.go @@ -0,0 +1,102 @@ +package check + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleRequestedDocumentAuthenticityCheckBuilder() { + docAuthCheck, err := NewRequestedDocumentAuthenticityCheckBuilder().WithManualCheckFallback().Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docAuthCheck) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ID_DOCUMENT_AUTHENTICITY","config":{"manual_check":"FALLBACK"}} +} + +func ExampleRequestedDocumentAuthenticityCheckBuilder_Build() { + docAuthCheck, err := NewRequestedDocumentAuthenticityCheckBuilder().Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docAuthCheck) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ID_DOCUMENT_AUTHENTICITY","config":{}} +} + +func TestRequestedDocumentAuthenticityCheckBuilder_WithManualCheckAlways(t *testing.T) { + docAuthCheck, err := NewRequestedDocumentAuthenticityCheckBuilder(). + WithManualCheckAlways(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + result := docAuthCheck.Config().(RequestedDocumentAuthenticityConfig) + assert.Equal(t, "ALWAYS", result.ManualCheck) +} + +func TestRequestedDocumentAuthenticityCheckBuilder_WithManualCheckFallback(t *testing.T) { + docAuthCheck, err := NewRequestedDocumentAuthenticityCheckBuilder(). + WithManualCheckFallback(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + result := docAuthCheck.Config().(RequestedDocumentAuthenticityConfig) + assert.Equal(t, "FALLBACK", result.ManualCheck) +} + +func TestRequestedDocumentAuthenticityCheckBuilder_WithManualCheckNever(t *testing.T) { + docAuthCheck, err := NewRequestedDocumentAuthenticityCheckBuilder(). + WithManualCheckNever(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + result := docAuthCheck.Config().(RequestedDocumentAuthenticityConfig) + assert.Equal(t, "NEVER", result.ManualCheck) +} + +func TestRequestedDocumentAuthenticityCheckBuilder_UsesLastValue(t *testing.T) { + docAuthCheck, err := NewRequestedDocumentAuthenticityCheckBuilder(). + WithManualCheckFallback(). + WithManualCheckNever(). + WithManualCheckAlways(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + result := docAuthCheck.Config().(RequestedDocumentAuthenticityConfig) + assert.Equal(t, "ALWAYS", result.ManualCheck) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison.go new file mode 100644 index 0000000..6c5b06b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison.go @@ -0,0 +1,56 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedIDDocumentComparisonCheck requests creation of a Document Comparison Check +type RequestedIDDocumentComparisonCheck struct { + config RequestedIDDocumentComparisonConfig +} + +// Type is the type of the Requested Check +func (c *RequestedIDDocumentComparisonCheck) Type() string { + return constants.IDDocumentComparison +} + +// Config is the configuration of the Requested Check +func (c *RequestedIDDocumentComparisonCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig( + c.config, + ) +} + +// MarshalJSON returns the JSON encoding +func (c *RequestedIDDocumentComparisonCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +// RequestedIDDocumentComparisonConfig is the configuration applied when creating a Document Comparison Check +type RequestedIDDocumentComparisonConfig struct { +} + +// RequestedIDDocumentComparisonCheckBuilder builds a RequestedIDDocumentComparisonCheck +type RequestedIDDocumentComparisonCheckBuilder struct { + config RequestedIDDocumentComparisonConfig +} + +// NewRequestedIDDocumentComparisonCheckBuilder creates a new DocumentComparisonCheckBuilder +func NewRequestedIDDocumentComparisonCheckBuilder() *RequestedIDDocumentComparisonCheckBuilder { + return &RequestedIDDocumentComparisonCheckBuilder{} +} + +// Build builds the RequestedIDDocumentComparisonCheck +func (b *RequestedIDDocumentComparisonCheckBuilder) Build() (*RequestedIDDocumentComparisonCheck, error) { + return &RequestedIDDocumentComparisonCheck{ + config: b.config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison_test.go new file mode 100644 index 0000000..1fa0752 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/document_comparison_test.go @@ -0,0 +1,24 @@ +package check + +import ( + "encoding/json" + "fmt" +) + +func ExampleNewRequestedIDDocumentComparisonCheckBuilder() { + check, err := NewRequestedIDDocumentComparisonCheckBuilder().Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ID_DOCUMENT_COMPARISON","config":{}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check.go new file mode 100644 index 0000000..caf783c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check.go @@ -0,0 +1,33 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedFaceComparisonCheck represents a face comparison check request. +type RequestedFaceComparisonCheck struct { + config RequestedFaceComparisonConfig +} + +// Type returns the type of the check. +func (c *RequestedFaceComparisonCheck) Type() string { + return constants.FaceComparison +} + +// Config returns the configuration of the check. +func (c *RequestedFaceComparisonCheck) Config() RequestedCheckConfig { + return c.config +} + +// MarshalJSON encodes the struct into JSON. +func (c *RequestedFaceComparisonCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_builder.go new file mode 100644 index 0000000..94cceb9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_builder.go @@ -0,0 +1,39 @@ +package check + +import "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + +// RequestedFaceComparisonCheckBuilder builds a RequestedFaceComparisonCheck. +type RequestedFaceComparisonCheckBuilder struct { + manualCheck string +} + +// NewRequestedFaceComparisonCheckBuilder creates a new builder. +func NewRequestedFaceComparisonCheckBuilder() *RequestedFaceComparisonCheckBuilder { + return &RequestedFaceComparisonCheckBuilder{} +} + +// WithManualCheckNever sets the manual check mode to NEVER. +func (b *RequestedFaceComparisonCheckBuilder) WithManualCheckNever() *RequestedFaceComparisonCheckBuilder { + b.manualCheck = constants.Never + return b +} + +func (b *RequestedFaceComparisonCheckBuilder) WithManualCheckAlways() *RequestedFaceComparisonCheckBuilder { + b.manualCheck = constants.Always + return b +} + +func (b *RequestedFaceComparisonCheckBuilder) WithManualCheckFallback() *RequestedFaceComparisonCheckBuilder { + b.manualCheck = constants.Fallback + return b +} + +// Build constructs the RequestedFaceComparisonCheck. +func (b *RequestedFaceComparisonCheckBuilder) Build() (*RequestedFaceComparisonCheck, error) { + config := RequestedFaceComparisonConfig{ + ManualCheck: b.manualCheck, + } + return &RequestedFaceComparisonCheck{ + config: config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_test.go new file mode 100644 index 0000000..1e18707 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_check_test.go @@ -0,0 +1,72 @@ +package check + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleRequestedFaceComparisonCheckBuilder() { + check, err := NewRequestedFaceComparisonCheckBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"FACE_COMPARISON","config":{"manual_check":"NEVER"}} +} + +func TestRequestedFaceComparisonCheckBuilder_WithManualCheckAlways(t *testing.T) { + check, err := NewRequestedFaceComparisonCheckBuilder(). + WithManualCheckAlways(). + Build() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + config := check.Config().(RequestedFaceComparisonConfig) + assert.Equal(t, "ALWAYS", config.ManualCheck) +} + +func TestRequestedFaceComparisonCheckBuilder_WithManualCheckFallback(t *testing.T) { + check, err := NewRequestedFaceComparisonCheckBuilder(). + WithManualCheckFallback(). + Build() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + config := check.Config().(RequestedFaceComparisonConfig) + assert.Equal(t, "FALLBACK", config.ManualCheck) +} + +func TestRequestedFaceComparisonCheckBuilder_WithManualCheckNever(t *testing.T) { + check, err := NewRequestedFaceComparisonCheckBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + config := check.Config().(RequestedFaceComparisonConfig) + assert.Equal(t, "NEVER", config.ManualCheck) +} + +func TestRequestedFaceComparisonCheckBuilder_DefaultManualCheckEmpty(t *testing.T) { + builder := NewRequestedFaceComparisonCheckBuilder() + + check, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, check.config.ManualCheck, "") // default is empty string +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_config.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_config.go new file mode 100644 index 0000000..9b77cca --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_comparison_config.go @@ -0,0 +1,6 @@ +package check + +// RequestedFaceComparisonConfig is the configuration for a face comparison check. +type RequestedFaceComparisonConfig struct { + ManualCheck string `json:"manual_check,omitempty"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match.go new file mode 100644 index 0000000..8142e61 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match.go @@ -0,0 +1,77 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedFaceMatchCheck requests creation of a FaceMatch Check +type RequestedFaceMatchCheck struct { + config RequestedFaceMatchConfig +} + +// Type is the type of the Requested Check +func (c *RequestedFaceMatchCheck) Type() string { + return constants.IDDocumentFaceMatch +} + +// Config is the configuration of the Requested Check +func (c *RequestedFaceMatchCheck) Config() RequestedCheckConfig { + return c.config +} + +// MarshalJSON returns the JSON encoding +func (c *RequestedFaceMatchCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +// RequestedFaceMatchConfig is the configuration applied when creating a FaceMatch Check +type RequestedFaceMatchConfig struct { + ManualCheck string `json:"manual_check,omitempty"` +} + +// NewRequestedFaceMatchCheckBuilder creates a new RequestedFaceMatchCheckBuilder +func NewRequestedFaceMatchCheckBuilder() *RequestedFaceMatchCheckBuilder { + return &RequestedFaceMatchCheckBuilder{} +} + +// RequestedFaceMatchCheckBuilder builds a RequestedFaceMatchCheck +type RequestedFaceMatchCheckBuilder struct { + manualCheck string +} + +// WithManualCheckAlways sets the value of manual check to "ALWAYS" +func (b *RequestedFaceMatchCheckBuilder) WithManualCheckAlways() *RequestedFaceMatchCheckBuilder { + b.manualCheck = constants.Always + return b +} + +// WithManualCheckFallback sets the value of manual check to "FALLBACK" +func (b *RequestedFaceMatchCheckBuilder) WithManualCheckFallback() *RequestedFaceMatchCheckBuilder { + b.manualCheck = constants.Fallback + return b +} + +// WithManualCheckNever sets the value of manual check to "NEVER" +func (b *RequestedFaceMatchCheckBuilder) WithManualCheckNever() *RequestedFaceMatchCheckBuilder { + b.manualCheck = constants.Never + return b +} + +// Build builds the RequestedFaceMatchCheck +func (b *RequestedFaceMatchCheckBuilder) Build() (*RequestedFaceMatchCheck, error) { + config := RequestedFaceMatchConfig{ + ManualCheck: b.manualCheck, + } + + return &RequestedFaceMatchCheck{ + config: config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match_test.go new file mode 100644 index 0000000..6686d66 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/face_match_test.go @@ -0,0 +1,64 @@ +package check + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleRequestedFaceMatchCheckBuilder() { + check, err := NewRequestedFaceMatchCheckBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ID_DOCUMENT_FACE_MATCH","config":{"manual_check":"NEVER"}} +} + +func TestRequestedFaceMatchCheckBuilder_WithManualCheckAlways(t *testing.T) { + task, err := NewRequestedFaceMatchCheckBuilder(). + WithManualCheckAlways(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedFaceMatchConfig) + assert.Equal(t, "ALWAYS", config.ManualCheck) +} + +func TestRequestedFaceMatchCheckBuilder_WithManualCheckFallback(t *testing.T) { + task, err := NewRequestedFaceMatchCheckBuilder(). + WithManualCheckFallback(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedFaceMatchConfig) + assert.Equal(t, "FALLBACK", config.ManualCheck) +} + +func TestRequestedFaceMatchCheckBuilder_WithManualCheckNever(t *testing.T) { + task, err := NewRequestedFaceMatchCheckBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedFaceMatchConfig) + assert.Equal(t, "NEVER", config.ManualCheck) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness.go new file mode 100644 index 0000000..0651946 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness.go @@ -0,0 +1,98 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedLivenessCheck requests creation of a Liveness Check +type RequestedLivenessCheck struct { + config RequestedLivenessConfig +} + +// Type is the type of the Requested Check +func (c *RequestedLivenessCheck) Type() string { + return constants.Liveness +} + +// Config is the configuration of the Requested Check +func (c *RequestedLivenessCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig( + c.config, + ) +} + +// MarshalJSON returns the JSON encoding +func (c *RequestedLivenessCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +// RequestedLivenessConfig is the configuration applied when creating a Liveness Check +type RequestedLivenessConfig struct { + MaxRetries int `json:"max_retries,omitempty"` + LivenessType string `json:"liveness_type,omitempty"` + ManualCheck string `json:"manual_check,omitempty"` +} + +// NewRequestedLivenessCheckBuilder creates a new RequestedLivenessCheckBuilder +func NewRequestedLivenessCheckBuilder() *RequestedLivenessCheckBuilder { + return &RequestedLivenessCheckBuilder{} +} + +// RequestedLivenessCheckBuilder builds a RequestedLivenessCheck +type RequestedLivenessCheckBuilder struct { + livenessType string + maxRetries int + manualCheck string +} + +// ForZoomLiveness sets the liveness type to "ZOOM" +func (b *RequestedLivenessCheckBuilder) ForZoomLiveness() *RequestedLivenessCheckBuilder { + return b.ForLivenessType(zoom) +} + +// ForStaticLiveness sets the liveness type to "STATIC" +func (b *RequestedLivenessCheckBuilder) ForStaticLiveness() *RequestedLivenessCheckBuilder { + return b.ForLivenessType(static) +} + +// ForLivenessType sets the liveness type on the builder +func (b *RequestedLivenessCheckBuilder) ForLivenessType(livenessType string) *RequestedLivenessCheckBuilder { + b.livenessType = livenessType + if livenessType == constants.Static { + b.WithManualCheckNever() + } + return b +} + +// WithMaxRetries sets the maximum number of retries allowed for liveness check on the builder +func (b *RequestedLivenessCheckBuilder) WithMaxRetries(maxRetries int) *RequestedLivenessCheckBuilder { + b.maxRetries = maxRetries + return b +} + +// WithManualCheckNever sets the value of manual check to "NEVER" +func (b *RequestedLivenessCheckBuilder) WithManualCheckNever() *RequestedLivenessCheckBuilder { + b.manualCheck = constants.Never + return b +} + +// Build builds the RequestedLivenessCheck +func (b *RequestedLivenessCheckBuilder) Build() (*RequestedLivenessCheck, error) { + config := RequestedLivenessConfig{ + MaxRetries: b.maxRetries, + LivenessType: b.livenessType, + ManualCheck: b.manualCheck, + } + + return &RequestedLivenessCheck{ + config: config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness_test.go new file mode 100644 index 0000000..ae03035 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/liveness_test.go @@ -0,0 +1,64 @@ +package check + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleRequestedLivenessCheckBuilder() { + check, err := NewRequestedLivenessCheckBuilder(). + ForZoomLiveness(). + WithMaxRetries(9). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"LIVENESS","config":{"max_retries":9,"liveness_type":"ZOOM"}} + +} + +func TestExampleRequestedStaticLivenessCheckBuilder(t *testing.T) { + check, err := NewRequestedLivenessCheckBuilder(). + ForStaticLiveness(). + WithMaxRetries(5). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(check) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"LIVENESS","config":{"max_retries":5,"liveness_type":"STATIC"}} +} + +func TestRequestedLivenessCheckBuilder_MaxRetriesIsOmittedIfNotSet(t *testing.T) { + check, err := NewRequestedLivenessCheckBuilder(). + ForLivenessType("LIVENESS_TYPE"). + Build() + + assert.NilError(t, err) + + result, err := json.Marshal(check) + assert.NilError(t, err) + + expected := "{\"type\":\"LIVENESS\",\"config\":{\"liveness_type\":\"LIVENESS_TYPE\"}}" + assert.Equal(t, expected, string(result)) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/requested_check.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/requested_check.go new file mode 100644 index 0000000..2da6f9d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/requested_check.go @@ -0,0 +1,12 @@ +package check + +// RequestedCheck requests creation of a Check to be performed on a document +type RequestedCheck interface { + Type() string + Config() RequestedCheckConfig + MarshalJSON() ([]byte, error) +} + +// RequestedCheckConfig is the configuration applied when creating a Check +type RequestedCheckConfig interface { +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity.go new file mode 100644 index 0000000..6af6d40 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity.go @@ -0,0 +1,55 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedThirdPartyIdentityCheck requests creation of a third party CRA check +type RequestedThirdPartyIdentityCheck struct { + config RequestedThirdPartyIdentityCheckConfig +} + +// Type is the type of the requested check +func (c *RequestedThirdPartyIdentityCheck) Type() string { + return constants.ThirdPartyIdentityCheck +} + +// Config is the configuration of the requested check +func (c *RequestedThirdPartyIdentityCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig(c.config) +} + +// MarshalJSON returns the JSON encoding +func (c *RequestedThirdPartyIdentityCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +// RequestedThirdPartyIdentityCheckConfig is the configuration applied when creating +// a third party identity check +type RequestedThirdPartyIdentityCheckConfig struct { +} + +// RequestedThirdPartyIdentityCheckBuilder builds a RequestedThirdPartyIdentityCheck +type RequestedThirdPartyIdentityCheckBuilder struct { + config RequestedThirdPartyIdentityCheckConfig +} + +// NewRequestedThirdPartyIdentityCheckBuilder creates a new builder for RequestedThirdPartyIdentityCheck +func NewRequestedThirdPartyIdentityCheckBuilder() *RequestedThirdPartyIdentityCheckBuilder { + return &RequestedThirdPartyIdentityCheckBuilder{} +} + +// Build builds the RequestedThirdPartyIdentityCheck +func (b *RequestedThirdPartyIdentityCheckBuilder) Build() (*RequestedThirdPartyIdentityCheck, error) { + return &RequestedThirdPartyIdentityCheck{ + config: b.config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity_test.go new file mode 100644 index 0000000..4a8c7fc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/third_party_identity_test.go @@ -0,0 +1,23 @@ +package check + +import ( + "encoding/json" + "fmt" +) + +func ExampleRequestedThirdPartyIdentityCheck() { + thirdPartyCheck, err := NewRequestedThirdPartyIdentityCheckBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(thirdPartyCheck) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"THIRD_PARTY_IDENTITY","config":{}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca.go new file mode 100644 index 0000000..c2ccd69 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca.go @@ -0,0 +1,9 @@ +package check + +type RequestedWatchlistAdvancedCAConfig struct { + Type string `json:"type,omitempty"` + RemoveDeceased bool `json:"remove_deceased,omitempty"` + ShareUrl bool `json:"share_url,omitempty"` + Sources RequestedCASources `json:"sources,omitempty"` + MatchingStrategy RequestedCAMatchingStrategy `json:"matching_strategy,omitempty"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom.go new file mode 100644 index 0000000..61e6aa3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom.go @@ -0,0 +1,120 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +type RequestedWatchlistAdvancedCACustomAccountCheck struct { + config RequestedWatchlistAdvancedCACustomAccountConfig +} + +// Type is the type of the requested check +func (c RequestedWatchlistAdvancedCACustomAccountCheck) Type() string { + return constants.WatchlistAdvancedCA +} + +// Config is the configuration of the requested check +func (c RequestedWatchlistAdvancedCACustomAccountCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig(c.config) +} + +// MarshalJSON returns the JSON encoding +func (c RequestedWatchlistAdvancedCACustomAccountCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +func NewRequestedWatchlistAdvancedCACheckCustomAccountBuilder() *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + return &RequestedWatchlistAdvancedCACheckCustomAccountBuilder{} +} + +type RequestedWatchlistAdvancedCACustomAccountConfig struct { + RequestedWatchlistAdvancedCAConfig + APIKey string `json:"api_key,omitempty"` + Monitoring bool `json:"monitoring,omitempty"` + Tags map[string]string `json:"tags,omitempty"` + ClientRef string `json:"client_ref,omitempty"` +} + +type RequestedWatchlistAdvancedCACheckCustomAccountBuilder struct { + removeDeceased bool + shareURL bool + requestedCASources RequestedCASources + requestedCAMatchingStrategy RequestedCAMatchingStrategy + apiKey string + monitoring bool + tags map[string]string + clientRef string +} + +// WithAPIKey sets the API key for the Watchlist Advanced CA check (custom account). +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithAPIKey(apiKey string) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.apiKey = apiKey + return b +} + +// WithMonitoring sets whether monitoring is used for the Watchlist Advanced CA check (custom account). +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithMonitoring(monitoring bool) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.monitoring = monitoring + return b +} + +// WithTags sets tags used for custom account Watchlist Advanced CA check. +// Please note this will override any previously set tags +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithTags(tags map[string]string) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.tags = tags + return b +} + +// WithClientRef sets the client reference for the Watchlist Advanced CA check (custom account). +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithClientRef(clientRef string) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.clientRef = clientRef + return b +} + +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithRemoveDeceased(removeDeceased bool) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.removeDeceased = removeDeceased + return b +} + +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithShareURL(shareURL bool) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.shareURL = shareURL + return b +} + +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithSources(requestedCASources RequestedCASources) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.requestedCASources = requestedCASources + return b +} + +func (b *RequestedWatchlistAdvancedCACheckCustomAccountBuilder) WithMatchingStrategy(requestedCAMatchingStrategy RequestedCAMatchingStrategy) *RequestedWatchlistAdvancedCACheckCustomAccountBuilder { + b.requestedCAMatchingStrategy = requestedCAMatchingStrategy + return b +} + +func (b RequestedWatchlistAdvancedCACheckCustomAccountBuilder) Build() (RequestedWatchlistAdvancedCACustomAccountCheck, error) { + config := RequestedWatchlistAdvancedCACustomAccountConfig{ + RequestedWatchlistAdvancedCAConfig: RequestedWatchlistAdvancedCAConfig{ + Type: constants.WithCustomAccount, + RemoveDeceased: b.removeDeceased, + ShareUrl: b.shareURL, + Sources: b.requestedCASources, + MatchingStrategy: b.requestedCAMatchingStrategy, + }, + APIKey: b.apiKey, + Monitoring: b.monitoring, + Tags: b.tags, + ClientRef: b.clientRef, + } + + return RequestedWatchlistAdvancedCACustomAccountCheck{ + config: config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom_test.go new file mode 100644 index 0000000..7478ac7 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_custom_test.go @@ -0,0 +1,32 @@ +package check_test + +import ( + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/check" +) + +func ExampleRequestedWatchlistAdvancedCACheckCustomAccountBuilder_Build() { + advancedCACustomAccountCheck, err := check.NewRequestedWatchlistAdvancedCACheckCustomAccountBuilder(). + WithAPIKey("api-key"). + WithMonitoring(true). + WithTags(map[string]string{ + "tag_name": "value", + }). + WithClientRef("client-ref"). + WithMatchingStrategy(check.RequestedExactMatchingStrategy{ExactMatch: true}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := advancedCACustomAccountCheck.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"WATCHLIST_ADVANCED_CA","config":{"type":"WITH_CUSTOM_ACCOUNT","matching_strategy":{"type":"EXACT","exact_match":true},"api_key":"api-key","monitoring":true,"tags":{"tag_name":"value"},"client_ref":"client-ref"}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_matching_strategy.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_matching_strategy.go new file mode 100644 index 0000000..136381b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_matching_strategy.go @@ -0,0 +1,48 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedCAMatchingStrategy is the base type which other CA matching strategies must satisfy +type RequestedCAMatchingStrategy interface { + Type() string +} + +func NewRequestedFuzzyMatchingStrategy() *RequestedFuzzyMatchingStrategy { + return &RequestedFuzzyMatchingStrategy{} +} + +type RequestedFuzzyMatchingStrategy struct { + RequestedCAMatchingStrategy + Fuzziness float64 +} + +// MarshalJSON returns the JSON encoding +func (c RequestedFuzzyMatchingStrategy) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Fuzziness float64 `json:"fuzziness"` + }{ + Type: constants.Fuzzy, + Fuzziness: c.Fuzziness, + }) +} + +type RequestedExactMatchingStrategy struct { + RequestedCAMatchingStrategy + ExactMatch bool +} + +// MarshalJSON returns the JSON encoding +func (c RequestedExactMatchingStrategy) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + ExactMatch bool `json:"exact_match"` + }{ + Type: constants.Exact, + ExactMatch: c.ExactMatch, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_sources.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_sources.go new file mode 100644 index 0000000..cd209f0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_sources.go @@ -0,0 +1,34 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedCASources is the base type which other CA sources must satisfy +type RequestedCASources interface { + Type() string + MarshalJSON() ([]byte, error) +} + +type RequestedTypeListSources struct { + RequestedCASources + Types []string +} + +// Type is the type of the Requested Check +func (c RequestedTypeListSources) Type() string { + return constants.TypeList +} + +// MarshalJSON returns the JSON encoding +func (c RequestedTypeListSources) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Types []string `json:"types"` + }{ + Type: c.Type(), + Types: c.Types, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti.go new file mode 100644 index 0000000..407d9ce --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti.go @@ -0,0 +1,84 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +type RequestedWatchlistAdvancedCAYotiAccountCheck struct { + config RequestedWatchlistAdvancedCAYotiAccountConfig +} + +// Type is the type of the requested check +func (c RequestedWatchlistAdvancedCAYotiAccountCheck) Type() string { + return constants.WatchlistAdvancedCA +} + +// Config is the configuration of the requested check +func (c RequestedWatchlistAdvancedCAYotiAccountCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig(c.config) +} + +// MarshalJSON returns the JSON encoding +func (c RequestedWatchlistAdvancedCAYotiAccountCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +type RequestedWatchlistAdvancedCACheckYotiAccountBuilder struct { + removeDeceased bool + shareURL bool + requestedCASources RequestedCASources + requestedCAMatchingStrategy RequestedCAMatchingStrategy +} + +type RequestedWatchlistAdvancedCAYotiAccountConfig struct { + RequestedWatchlistAdvancedCAConfig +} + +// NewRequestedWatchlistAdvancedCACheckYotiAccountBuilder creates a new builder for RequestedWatchlistAdvancedCACheckYotiAccountBuilder +func NewRequestedWatchlistAdvancedCACheckYotiAccountBuilder() *RequestedWatchlistAdvancedCACheckYotiAccountBuilder { + return &RequestedWatchlistAdvancedCACheckYotiAccountBuilder{} +} + +func (b *RequestedWatchlistAdvancedCACheckYotiAccountBuilder) WithRemoveDeceased(removeDeceased bool) *RequestedWatchlistAdvancedCACheckYotiAccountBuilder { + b.removeDeceased = removeDeceased + return b +} + +func (b *RequestedWatchlistAdvancedCACheckYotiAccountBuilder) WithShareURL(shareURL bool) *RequestedWatchlistAdvancedCACheckYotiAccountBuilder { + b.shareURL = shareURL + return b +} + +func (b *RequestedWatchlistAdvancedCACheckYotiAccountBuilder) WithSources(requestedCASources RequestedCASources) *RequestedWatchlistAdvancedCACheckYotiAccountBuilder { + b.requestedCASources = requestedCASources + return b +} + +func (b *RequestedWatchlistAdvancedCACheckYotiAccountBuilder) WithMatchingStrategy(requestedCAMatchingStrategy RequestedCAMatchingStrategy) *RequestedWatchlistAdvancedCACheckYotiAccountBuilder { + b.requestedCAMatchingStrategy = requestedCAMatchingStrategy + return b +} + +func (b RequestedWatchlistAdvancedCACheckYotiAccountBuilder) Build() (RequestedWatchlistAdvancedCAYotiAccountCheck, error) { + config := RequestedWatchlistAdvancedCAYotiAccountConfig{ + RequestedWatchlistAdvancedCAConfig{ + Type: constants.WithYotiAccounts, + RemoveDeceased: b.removeDeceased, + ShareUrl: b.shareURL, + Sources: b.requestedCASources, + MatchingStrategy: b.requestedCAMatchingStrategy, + }, + } + + return RequestedWatchlistAdvancedCAYotiAccountCheck{ + config: config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti_test.go new file mode 100644 index 0000000..112d464 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_advanced_ca_yoti_test.go @@ -0,0 +1,30 @@ +package check_test + +import ( + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/check" +) + +func ExampleNewRequestedWatchlistAdvancedCACheckYotiAccountBuilder() { + advancedCAYotiAccountCheck, err := check.NewRequestedWatchlistAdvancedCACheckYotiAccountBuilder(). + WithRemoveDeceased(true). + WithShareURL(true). + WithSources(check.RequestedTypeListSources{ + Types: []string{"pep", "fitness-probity", "warning"}}). + WithMatchingStrategy(check.RequestedFuzzyMatchingStrategy{Fuzziness: 0.5}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := advancedCAYotiAccountCheck.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"WATCHLIST_ADVANCED_CA","config":{"type":"WITH_YOTI_ACCOUNT","remove_deceased":true,"share_url":true,"sources":{"type":"TYPE_LIST","types":["pep","fitness-probity","warning"]},"matching_strategy":{"type":"FUZZY","fuzziness":0.5}}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening.go new file mode 100644 index 0000000..9172b37 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening.go @@ -0,0 +1,77 @@ +package check + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedWatchlistScreeningCheck requests creation of a Watchlist Screening Check. +// To request a RequestedWatchlistScreeningCheck you must request task.RequestedTextExtractionTask as a minimum +type RequestedWatchlistScreeningCheck struct { + config RequestedWatchlistScreeningCheckConfig +} + +// Type is the type of the requested check +func (c *RequestedWatchlistScreeningCheck) Type() string { + return constants.WatchlistScreening +} + +// Config is the configuration of the requested check +func (c *RequestedWatchlistScreeningCheck) Config() RequestedCheckConfig { + return RequestedCheckConfig(c.config) +} + +// MarshalJSON returns the JSON encoding +func (c *RequestedWatchlistScreeningCheck) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedCheckConfig `json:"config,omitempty"` + }{ + Type: c.Type(), + Config: c.Config(), + }) +} + +// RequestedWatchlistScreeningCheckConfig is the configuration applied when creating +// a watchlist screening check +type RequestedWatchlistScreeningCheckConfig struct { + Categories []string `json:"categories"` +} + +// RequestedWatchlistScreeningCheckBuilder builds a RequestedWatchlistScreeningCheck +type RequestedWatchlistScreeningCheckBuilder struct { + categories []string +} + +// NewRequestedWatchlistScreeningCheckBuilder creates a new builder for RequestedWatchlistScreeningCheck +func NewRequestedWatchlistScreeningCheckBuilder() *RequestedWatchlistScreeningCheckBuilder { + return &RequestedWatchlistScreeningCheckBuilder{} +} + +// WithCategory adds a category to the list of categories used for watchlist screening +func (b *RequestedWatchlistScreeningCheckBuilder) WithCategory(category string) *RequestedWatchlistScreeningCheckBuilder { + b.categories = append(b.categories, category) + return b +} + +// WithAdverseMediaCategory adds ADVERSE_MEDIA to the list of categories used for watchlist screening +func (b *RequestedWatchlistScreeningCheckBuilder) WithAdverseMediaCategory() *RequestedWatchlistScreeningCheckBuilder { + return b.WithCategory(constants.AdverseMedia) +} + +// WithSanctionsCategory adds SANCTIONS to the list of categories used for watchlist screening +func (b *RequestedWatchlistScreeningCheckBuilder) WithSanctionsCategory() *RequestedWatchlistScreeningCheckBuilder { + return b.WithCategory(constants.Sanctions) +} + +// Build builds the RequestedWatchlistScreeningCheck +func (b *RequestedWatchlistScreeningCheckBuilder) Build() (*RequestedWatchlistScreeningCheck, error) { + config := RequestedWatchlistScreeningCheckConfig{ + Categories: b.categories, + } + + return &RequestedWatchlistScreeningCheck{ + config: config, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening_test.go new file mode 100644 index 0000000..81e8096 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/check/watchlist_screening_test.go @@ -0,0 +1,26 @@ +package check + +import ( + "encoding/json" + "fmt" +) + +func ExampleNewRequestedWatchlistScreeningCheckBuilder() { + watchlistScreeningCheck, err := NewRequestedWatchlistScreeningCheckBuilder(). + WithAdverseMediaCategory(). + WithSanctionsCategory(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(watchlistScreeningCheck) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"WATCHLIST_SCREENING","config":{"categories":["ADVERSE-MEDIA","SANCTIONS"]}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/constants.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/constants.go new file mode 100644 index 0000000..cd69836 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/constants.go @@ -0,0 +1,6 @@ +package create + +const ( + reclassification string = "RECLASSIFICATION" + generic string = "GENERIC" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/create_session_result.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/create_session_result.go new file mode 100644 index 0000000..38b900a --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/create_session_result.go @@ -0,0 +1,8 @@ +package create + +// SessionResult contains the information about a created session +type SessionResult struct { + ClientSessionTokenTTL int `json:"client_session_token_ttl"` + ClientSessionToken string `json:"client_session_token"` + SessionID string `json:"session_id"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource.go new file mode 100644 index 0000000..e3f57a1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource.go @@ -0,0 +1,12 @@ +package facecapture + +type CreateFaceCaptureResourcePayload struct { + RequirementID string `json:"requirement_id"` +} + +// NewCreateFaceCaptureResourcePayload creates a new payload with the given requirement ID. +func NewCreateFaceCaptureResourcePayload(requirementID string) *CreateFaceCaptureResourcePayload { + return &CreateFaceCaptureResourcePayload{ + RequirementID: requirementID, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource_test.go new file mode 100644 index 0000000..73992a1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/face_capture_resource_test.go @@ -0,0 +1,15 @@ +package facecapture + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestNewCreateFaceCaptureResourcePayload(t *testing.T) { + requirementID := "test-requirement-id" + payload := NewCreateFaceCaptureResourcePayload(requirementID) + + assert.Assert(t, payload != nil) + assert.Equal(t, payload.RequirementID, requirementID) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload.go new file mode 100644 index 0000000..4fe053e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload.go @@ -0,0 +1,65 @@ +package facecapture + +import ( + "bytes" + "fmt" + "mime/multipart" + "net/textproto" +) + +// UploadFaceCaptureImagePayload represents the payload for uploading a face capture image. +// It includes the image's content type and binary data, and provides methods to prepare +// the payload as a multipart form body. +type UploadFaceCaptureImagePayload struct { + ImageContentType string + ImageContents []byte + + body *bytes.Buffer + writer *multipart.Writer +} + +func NewUploadFaceCaptureImagePayload(contentType string, contents []byte) *UploadFaceCaptureImagePayload { + return &UploadFaceCaptureImagePayload{ + ImageContentType: contentType, + ImageContents: contents, + } +} + +func (p *UploadFaceCaptureImagePayload) Prepare() error { + if p.ImageContentType == "" { + return fmt.Errorf("ImageContentType must not be empty") + } + if len(p.ImageContents) == 0 { + return fmt.Errorf("ImageContents must not be empty") + } + + p.body = &bytes.Buffer{} + p.writer = multipart.NewWriter(p.body) + + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", `form-data; name="binary-content"; filename="face-capture-image"`) + header.Set("Content-Type", p.ImageContentType) + + part, err := p.writer.CreatePart(header) + if err != nil { + return fmt.Errorf("failed to create multipart part: %w", err) + } + + _, err = part.Write(p.ImageContents) + if err != nil { + return fmt.Errorf("failed to write image contents: %w", err) + } + + return p.writer.Close() +} + +func (p *UploadFaceCaptureImagePayload) MultipartFormBody() *bytes.Buffer { + return p.body +} + +// Fixed return type to match SignedRequest expectations +func (p *UploadFaceCaptureImagePayload) Headers() map[string][]string { + return map[string][]string{ + "Content-Type": {p.writer.FormDataContentType()}, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload_test.go new file mode 100644 index 0000000..bf4dd71 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/facecapture/upload_face_capture_image_payload_test.go @@ -0,0 +1,72 @@ +package facecapture + +import ( + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +func TestNewUploadFaceCaptureImagePayload(t *testing.T) { + contentType := "image/png" + content := []byte{1, 2, 3} + + payload := NewUploadFaceCaptureImagePayload(contentType, content) + + assert.Equal(t, payload.ImageContentType, contentType) + assert.DeepEqual(t, payload.ImageContents, content) +} + +func TestPrepare_ValidPayload(t *testing.T) { + contentType := "image/jpeg" + content := []byte{0x01, 0x02, 0x03} + + payload := NewUploadFaceCaptureImagePayload(contentType, content) + err := payload.Prepare() + + assert.NilError(t, err) + assert.Assert(t, payload.body != nil) + assert.Assert(t, payload.writer != nil) + + // Multipart body should contain the image bytes + bodyStr := payload.body.String() + assert.Assert(t, strings.Contains(bodyStr, "Content-Disposition")) + assert.Assert(t, strings.Contains(bodyStr, contentType)) + assert.Assert(t, strings.Contains(bodyStr, string(content))) +} + +func TestPrepare_EmptyContentType(t *testing.T) { + payload := NewUploadFaceCaptureImagePayload("", []byte{1, 2, 3}) + err := payload.Prepare() + + assert.ErrorContains(t, err, "ImageContentType must not be empty") +} + +func TestPrepare_EmptyContents(t *testing.T) { + payload := NewUploadFaceCaptureImagePayload("image/png", []byte{}) + err := payload.Prepare() + + assert.ErrorContains(t, err, "ImageContents must not be empty") +} + +func TestHeaders_ReturnsContentType(t *testing.T) { + payload := NewUploadFaceCaptureImagePayload("image/png", []byte{1, 2, 3}) + err := payload.Prepare() + assert.NilError(t, err) + + headers := payload.Headers() + contentTypes, ok := headers["Content-Type"] + assert.Assert(t, ok) + assert.Assert(t, len(contentTypes) > 0) + assert.Assert(t, strings.HasPrefix(contentTypes[0], "multipart/form-data; boundary=")) +} + +func TestMultipartFormBody_ReturnsBody(t *testing.T) { + payload := NewUploadFaceCaptureImagePayload("image/png", []byte{1, 2, 3}) + err := payload.Prepare() + assert.NilError(t, err) + + body := payload.MultipartFormBody() + assert.Assert(t, body != nil) + assert.Assert(t, body.Len() > 0) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/constants.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/constants.go new file mode 100644 index 0000000..b4ab855 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/constants.go @@ -0,0 +1,12 @@ +package filter + +const ( + includeList string = "WHITELIST" + excludeList string = "BLACKLIST" + + identityDocument string = "ID_DOCUMENT" + supplementaryDocument string = "SUPPLEMENTARY_DOCUMENT" + + orthogonalRestriction string = "ORTHOGONAL_RESTRICTIONS" + documentRestriction string = "DOCUMENT_RESTRICTIONS" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_filter.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_filter.go new file mode 100644 index 0000000..c2c8d2f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_filter.go @@ -0,0 +1,6 @@ +package filter + +// RequestedDocumentFilter filters for a required document, allowing specification of restrictive parameters +type RequestedDocumentFilter interface { + Type() string +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction.go new file mode 100644 index 0000000..9a9e531 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction.go @@ -0,0 +1,41 @@ +package filter + +// RequestedDocumentRestriction represents a document filter for checks and tasks +type RequestedDocumentRestriction struct { + DocumentTypes []string `json:"document_types,omitempty"` + CountryCodes []string `json:"country_codes,omitempty"` +} + +// RequestedDocumentRestrictionBuilder builds a RequestedDocumentRestriction +type RequestedDocumentRestrictionBuilder struct { + documentTypes []string + countryCodes []string +} + +// NewRequestedDocumentRestrictionBuilder creates a new RequestedDocumentRestrictionBuilder +func NewRequestedDocumentRestrictionBuilder() *RequestedDocumentRestrictionBuilder { + return &RequestedDocumentRestrictionBuilder{ + documentTypes: []string{}, + countryCodes: []string{}, + } +} + +// WithCountryCodes sets the country codes of the filter +func (b *RequestedDocumentRestrictionBuilder) WithCountryCodes(countryCodes []string) *RequestedDocumentRestrictionBuilder { + b.countryCodes = countryCodes + return b +} + +// WithDocumentTypes sets the document types of the filter +func (b *RequestedDocumentRestrictionBuilder) WithDocumentTypes(documentTypes []string) *RequestedDocumentRestrictionBuilder { + b.documentTypes = documentTypes + return b +} + +// Build creates a new RequestedDocumentRestriction +func (b *RequestedDocumentRestrictionBuilder) Build() (*RequestedDocumentRestriction, error) { + return &RequestedDocumentRestriction{ + DocumentTypes: b.documentTypes, + CountryCodes: b.countryCodes, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction_test.go new file mode 100644 index 0000000..67e4122 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restriction_test.go @@ -0,0 +1,64 @@ +package filter + +import ( + "encoding/json" + "fmt" +) + +func ExampleRequestedDocumentRestriction() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithDocumentTypes([]string{"PASSPORT"}). + WithCountryCodes([]string{"GBR"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docRestriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_types":["PASSPORT"],"country_codes":["GBR"]} +} + +func ExampleRequestedDocumentRestrictionBuilder_WithDocumentTypes() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithDocumentTypes([]string{"PASSPORT"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docRestriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"document_types":["PASSPORT"]} +} + +func ExampleRequestedDocumentRestrictionBuilder_WithCountryCodes() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithCountryCodes([]string{"GBR"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docRestriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"country_codes":["GBR"]} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter.go new file mode 100644 index 0000000..ee3b9ef --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter.go @@ -0,0 +1,88 @@ +package filter + +import "encoding/json" + +// RequestedDocumentRestrictionsFilter filters for a required document, allowing specification of restrictive parameters +type RequestedDocumentRestrictionsFilter struct { + inclusion string + documents []*RequestedDocumentRestriction + allowExpiredDocuments *bool + allowNonLatinDocuments *bool +} + +// Type is the type of the document restriction filter +func (r RequestedDocumentRestrictionsFilter) Type() string { + return documentRestriction +} + +// MarshalJSON returns the JSON encoding +func (r *RequestedDocumentRestrictionsFilter) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Inclusion string `json:"inclusion"` + Documents []*RequestedDocumentRestriction `json:"documents"` + AllowExpiredDocuments *bool `json:"allow_expired_documents,omitempty"` + AllowNonLatinDocuments *bool `json:"allow_non_latin_documents,omitempty"` + }{ + Type: r.Type(), + Inclusion: r.inclusion, + Documents: r.documents, + AllowExpiredDocuments: r.allowExpiredDocuments, + AllowNonLatinDocuments: r.allowNonLatinDocuments, + }) +} + +// RequestedDocumentRestrictionsFilterBuilder builds a RequestedDocumentRestrictionsFilter +type RequestedDocumentRestrictionsFilterBuilder struct { + inclusion string + documents []*RequestedDocumentRestriction + allowExpiredDocuments *bool + allowNonLatinDocuments *bool +} + +// NewRequestedDocumentRestrictionsFilterBuilder creates a new RequestedDocumentRestrictionsFilterBuilder +func NewRequestedDocumentRestrictionsFilterBuilder() *RequestedDocumentRestrictionsFilterBuilder { + return &RequestedDocumentRestrictionsFilterBuilder{ + documents: []*RequestedDocumentRestriction{}, + } +} + +// ForIncludeList sets the type restriction to INCLUDE the document restrictions +func (b *RequestedDocumentRestrictionsFilterBuilder) ForIncludeList() *RequestedDocumentRestrictionsFilterBuilder { + b.inclusion = includeList + return b +} + +// ForExcludeList sets the type restriction to EXCLUDE the document restrictions +func (b *RequestedDocumentRestrictionsFilterBuilder) ForExcludeList() *RequestedDocumentRestrictionsFilterBuilder { + b.inclusion = excludeList + return b +} + +// WithDocumentRestriction adds a document restriction to the filter +func (b *RequestedDocumentRestrictionsFilterBuilder) WithDocumentRestriction(docRestriction *RequestedDocumentRestriction) *RequestedDocumentRestrictionsFilterBuilder { + b.documents = append(b.documents, docRestriction) + return b +} + +// WithExpiredDocuments sets a bool value to allowExpiredDocuments on filter +func (b *RequestedDocumentRestrictionsFilterBuilder) WithExpiredDocuments(allowExpiredDocuments bool) *RequestedDocumentRestrictionsFilterBuilder { + b.allowExpiredDocuments = &allowExpiredDocuments + return b +} + +// WithExpiredDocuments sets a bool value to allowExpiredDocuments on filter +func (b *RequestedDocumentRestrictionsFilterBuilder) WithAllowNonLatinDocuments(allowNonLatinDocuments bool) *RequestedDocumentRestrictionsFilterBuilder { + b.allowNonLatinDocuments = &allowNonLatinDocuments + return b +} + +// Build creates a new RequestedDocumentRestrictionsFilter +func (b *RequestedDocumentRestrictionsFilterBuilder) Build() (*RequestedDocumentRestrictionsFilter, error) { + return &RequestedDocumentRestrictionsFilter{ + b.inclusion, + b.documents, + b.allowExpiredDocuments, + b.allowNonLatinDocuments, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter_test.go new file mode 100644 index 0000000..997072c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/document_restrictions_filter_test.go @@ -0,0 +1,140 @@ +package filter + +import ( + "encoding/json" + "fmt" +) + +func ExampleRequestedDocumentRestrictionsFilterBuilder_ForIncludeList() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithDocumentTypes([]string{"PASSPORT"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var docFilter *RequestedDocumentRestrictionsFilter + docFilter, err = NewRequestedDocumentRestrictionsFilterBuilder(). + ForIncludeList(). + WithDocumentRestriction(docRestriction). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docFilter) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"DOCUMENT_RESTRICTIONS","inclusion":"WHITELIST","documents":[{"document_types":["PASSPORT"]}]} +} + +func ExampleRequestedDocumentRestrictionsFilterBuilder_ForExcludeList() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithDocumentTypes([]string{"PASSPORT"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var docFilter *RequestedDocumentRestrictionsFilter + docFilter, err = NewRequestedDocumentRestrictionsFilterBuilder(). + ForExcludeList(). + WithDocumentRestriction(docRestriction). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(docFilter) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"DOCUMENT_RESTRICTIONS","inclusion":"BLACKLIST","documents":[{"document_types":["PASSPORT"]}]} +} + +func ExampleRequestedDocumentRestrictionsFilterBuilder_withExpiredDocuments() { + restriction, err := NewRequestedDocumentRestrictionsFilterBuilder(). + WithExpiredDocuments(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"DOCUMENT_RESTRICTIONS","inclusion":"","documents":[],"allow_expired_documents":true} +} + +func ExampleRequestedDocumentRestrictionsFilterBuilder_withDenyExpiredDocuments() { + restriction, err := NewRequestedDocumentRestrictionsFilterBuilder(). + WithExpiredDocuments(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"DOCUMENT_RESTRICTIONS","inclusion":"","documents":[],"allow_expired_documents":false} +} + +func ExampleRequestedDocumentRestrictionsFilterBuilder_withAllowNonLatinDocuments() { + restriction, err := NewRequestedDocumentRestrictionsFilterBuilder(). + WithAllowNonLatinDocuments(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"DOCUMENT_RESTRICTIONS","inclusion":"","documents":[],"allow_non_latin_documents":true} +} + +func ExampleRequestedDocumentRestrictionsFilterBuilder_withDenyNonLatinDocuments() { + restriction, err := NewRequestedDocumentRestrictionsFilterBuilder(). + WithAllowNonLatinDocuments(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"DOCUMENT_RESTRICTIONS","inclusion":"","documents":[],"allow_non_latin_documents":false} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter.go new file mode 100644 index 0000000..2097ffd --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter.go @@ -0,0 +1,109 @@ +package filter + +import "encoding/json" + +// RequestedOrthogonalRestrictionsFilter filters for a required document, allowing specification of restrictive parameters +type RequestedOrthogonalRestrictionsFilter struct { + countryRestriction *CountryRestriction + typeRestriction *TypeRestriction + allowExpiredDocuments *bool + allowNonLatinDocuments *bool +} + +// Type returns the type of the RequestedOrthogonalRestrictionsFilter +func (r RequestedOrthogonalRestrictionsFilter) Type() string { + return orthogonalRestriction +} + +// MarshalJSON returns the JSON encoding +func (r RequestedOrthogonalRestrictionsFilter) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + CountryRestriction *CountryRestriction `json:"country_restriction,omitempty"` + TypeRestriction *TypeRestriction `json:"type_restriction,omitempty"` + AllowExpiredDocuments *bool `json:"allow_expired_documents,omitempty"` + AllowNonLatinDocuments *bool `json:"allow_non_latin_documents,omitempty"` + }{ + CountryRestriction: r.countryRestriction, + TypeRestriction: r.typeRestriction, + Type: r.Type(), + AllowExpiredDocuments: r.allowExpiredDocuments, + AllowNonLatinDocuments: r.allowNonLatinDocuments, + }) +} + +// RequestedOrthogonalRestrictionsFilterBuilder builds a RequestedOrthogonalRestrictionsFilter +type RequestedOrthogonalRestrictionsFilterBuilder struct { + countryRestriction *CountryRestriction + typeRestriction *TypeRestriction + allowExpiredDocuments *bool + allowNonLatinDocuments *bool +} + +// NewRequestedOrthogonalRestrictionsFilterBuilder creates a new RequestedOrthogonalRestrictionsFilterBuilder +func NewRequestedOrthogonalRestrictionsFilterBuilder() *RequestedOrthogonalRestrictionsFilterBuilder { + return &RequestedOrthogonalRestrictionsFilterBuilder{ + countryRestriction: nil, + typeRestriction: nil, + allowExpiredDocuments: nil, + allowNonLatinDocuments: nil, + } +} + +// WithIncludedCountries sets an "INCLUDE" slice of country codes on the filter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) WithIncludedCountries(countryCodes []string) *RequestedOrthogonalRestrictionsFilterBuilder { + b.countryRestriction = &CountryRestriction{ + includeList, + countryCodes, + } + return b +} + +// WithExcludedCountries sets an "EXCLUDE" slice of country codes on the filter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) WithExcludedCountries(countryCodes []string) *RequestedOrthogonalRestrictionsFilterBuilder { + b.countryRestriction = &CountryRestriction{ + excludeList, + countryCodes, + } + return b +} + +// WithIncludedDocumentTypes sets an "INCLUDE" slice of document types on the filter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) WithIncludedDocumentTypes(documentTypes []string) *RequestedOrthogonalRestrictionsFilterBuilder { + b.typeRestriction = &TypeRestriction{ + includeList, + documentTypes, + } + return b +} + +// WithExcludedDocumentTypes sets an "EXCLUDE" slice of document types on the filter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) WithExcludedDocumentTypes(documentTypes []string) *RequestedOrthogonalRestrictionsFilterBuilder { + b.typeRestriction = &TypeRestriction{ + excludeList, + documentTypes, + } + return b +} + +// WithNonLatinDocuments sets a bool value to allowNonLatinDocuments on filter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) WithNonLatinDocuments(allowNonLatinDocuments bool) *RequestedOrthogonalRestrictionsFilterBuilder { + b.allowNonLatinDocuments = &allowNonLatinDocuments + return b +} + +// WithExpiredDocuments sets a bool value to allowExpiredDocuments on filter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) WithExpiredDocuments(allowExpiredDocuments bool) *RequestedOrthogonalRestrictionsFilterBuilder { + b.allowExpiredDocuments = &allowExpiredDocuments + return b +} + +// Build creates a new RequestedOrthogonalRestrictionsFilter +func (b *RequestedOrthogonalRestrictionsFilterBuilder) Build() (*RequestedOrthogonalRestrictionsFilter, error) { + return &RequestedOrthogonalRestrictionsFilter{ + b.countryRestriction, + b.typeRestriction, + b.allowExpiredDocuments, + b.allowNonLatinDocuments, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter_test.go new file mode 100644 index 0000000..5a2d7fe --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/orthogonal_restrictions_filter_test.go @@ -0,0 +1,158 @@ +package filter + +import ( + "encoding/json" + "fmt" +) + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_WithIncludedCountries() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithIncludedCountries([]string{"KEN", "CIV"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","country_restriction":{"inclusion":"WHITELIST","country_codes":["KEN","CIV"]}} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_WithIncludedDocumentTypes() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithIncludedDocumentTypes([]string{"PASSPORT", "DRIVING_LICENCE"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","type_restriction":{"inclusion":"WHITELIST","document_types":["PASSPORT","DRIVING_LICENCE"]}} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_WithExcludedDocumentTypes() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithExcludedDocumentTypes([]string{"NATIONAL_ID", "PASS_CARD"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","type_restriction":{"inclusion":"BLACKLIST","document_types":["NATIONAL_ID","PASS_CARD"]}} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_WithExcludedCountries() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithExcludedCountries([]string{"CAN", "FJI"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","country_restriction":{"inclusion":"BLACKLIST","country_codes":["CAN","FJI"]}} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_withExpiredDocuments() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithExpiredDocuments(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","allow_expired_documents":true} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_withDenyExpiredDocuments() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithExpiredDocuments(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","allow_expired_documents":false} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_withNonLatinDocuments() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithNonLatinDocuments(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","allow_non_latin_documents":true} +} + +func ExampleRequestedOrthogonalRestrictionsFilterBuilder_withDenyNonLatinDocuments() { + restriction, err := NewRequestedOrthogonalRestrictionsFilterBuilder(). + WithNonLatinDocuments(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(restriction) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ORTHOGONAL_RESTRICTIONS","allow_non_latin_documents":false} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_document.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_document.go new file mode 100644 index 0000000..7d70924 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_document.go @@ -0,0 +1,7 @@ +package filter + +// RequiredDocument is a document to be required for the session +type RequiredDocument interface { + Type() string + MarshalJSON() ([]byte, error) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document.go new file mode 100644 index 0000000..827b20e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document.go @@ -0,0 +1,49 @@ +package filter + +import ( + "encoding/json" +) + +// RequiredIDDocument details a required identity document +type RequiredIDDocument struct { + Filter RequestedDocumentFilter +} + +// Type returns the type of the identity document +func (i *RequiredIDDocument) Type() string { + return identityDocument +} + +// MarshalJSON returns the JSON encoding +func (i *RequiredIDDocument) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Filter RequestedDocumentFilter `json:"filter,omitempty"` + }{ + Type: i.Type(), + Filter: i.Filter, + }) +} + +// NewRequiredIDDocumentBuilder creates a new RequiredIDDocumentBuilder +func NewRequiredIDDocumentBuilder() *RequiredIDDocumentBuilder { + return &RequiredIDDocumentBuilder{} +} + +// RequiredIDDocumentBuilder builds a RequiredIDDocument +type RequiredIDDocumentBuilder struct { + filter RequestedDocumentFilter +} + +// WithFilter sets the filter on the required ID document +func (r RequiredIDDocumentBuilder) WithFilter(filter RequestedDocumentFilter) RequiredIDDocumentBuilder { + r.filter = filter + return r +} + +// Build builds the RequiredIDDocument struct +func (r RequiredIDDocumentBuilder) Build() (*RequiredIDDocument, error) { + return &RequiredIDDocument{ + Filter: r.filter, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document_test.go new file mode 100644 index 0000000..6cab90b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_id_document_test.go @@ -0,0 +1,44 @@ +package filter + +import ( + "encoding/json" + "fmt" +) + +func ExampleRequiredIDDocument_MarshalJSON() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithDocumentTypes([]string{"PASSPORT"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var docFilter *RequestedDocumentRestrictionsFilter + docFilter, err = NewRequestedDocumentRestrictionsFilterBuilder(). + ForIncludeList(). + WithDocumentRestriction(docRestriction). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var requiredIDDocument *RequiredIDDocument + requiredIDDocument, err = NewRequiredIDDocumentBuilder(). + WithFilter(docFilter). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredIDDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ID_DOCUMENT","filter":{"type":"DOCUMENT_RESTRICTIONS","inclusion":"WHITELIST","documents":[{"document_types":["PASSPORT"]}]}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document.go new file mode 100644 index 0000000..897b2fc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document.go @@ -0,0 +1,84 @@ +package filter + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/objective" +) + +// RequiredSupplementaryDocument details a required supplementary document +type RequiredSupplementaryDocument struct { + Filter RequestedDocumentFilter + DocumentTypes []string + CountryCodes []string + Objective objective.Objective +} + +// Type returns the type of the supplementary document +func (r *RequiredSupplementaryDocument) Type() string { + return supplementaryDocument +} + +// MarshalJSON returns the JSON encoding +func (r *RequiredSupplementaryDocument) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Filter RequestedDocumentFilter `json:"filter,omitempty"` + CountryCodes []string `json:"country_codes,omitempty"` + DocumentTypes []string `json:"document_types,omitempty"` + Objective objective.Objective `json:"objective,omitempty"` + }{ + Type: r.Type(), + Filter: r.Filter, + CountryCodes: r.CountryCodes, + DocumentTypes: r.DocumentTypes, + Objective: r.Objective, + }) +} + +// NewRequiredSupplementaryDocumentBuilder creates a new RequiredSupplementaryDocumentBuilder +func NewRequiredSupplementaryDocumentBuilder() *RequiredSupplementaryDocumentBuilder { + return &RequiredSupplementaryDocumentBuilder{} +} + +// RequiredSupplementaryDocumentBuilder builds a RequiredSupplementaryDocument +type RequiredSupplementaryDocumentBuilder struct { + filter RequestedDocumentFilter + documentTypes []string + countryCodes []string + objective objective.Objective +} + +// WithFilter sets the filter on the required supplementary document +func (r *RequiredSupplementaryDocumentBuilder) WithFilter(filter RequestedDocumentFilter) *RequiredSupplementaryDocumentBuilder { + r.filter = filter + return r +} + +// WithCountryCodes sets the country codes on the required supplementary document +func (r *RequiredSupplementaryDocumentBuilder) WithCountryCodes(countryCodes []string) *RequiredSupplementaryDocumentBuilder { + r.countryCodes = countryCodes + return r +} + +// WithDocumentTypes sets the document types on the required supplementary document +func (r *RequiredSupplementaryDocumentBuilder) WithDocumentTypes(documentTypes []string) *RequiredSupplementaryDocumentBuilder { + r.documentTypes = documentTypes + return r +} + +// WithObjective sets the objective for the required supplementary document +func (r *RequiredSupplementaryDocumentBuilder) WithObjective(objective objective.Objective) *RequiredSupplementaryDocumentBuilder { + r.objective = objective + return r +} + +// Build builds the RequiredSupplementaryDocument struct +func (r *RequiredSupplementaryDocumentBuilder) Build() (*RequiredSupplementaryDocument, error) { + return &RequiredSupplementaryDocument{ + Filter: r.filter, + DocumentTypes: r.documentTypes, + CountryCodes: r.countryCodes, + Objective: r.objective, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document_test.go new file mode 100644 index 0000000..d50b95c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/required_supplementary_document_test.go @@ -0,0 +1,207 @@ +package filter + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/objective" +) + +func ExampleRequiredSupplementaryDocument() { + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err := NewRequiredSupplementaryDocumentBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT"} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithFilter() { + docRestriction, err := NewRequestedDocumentRestrictionBuilder(). + WithDocumentTypes([]string{"UTILITY_BILL"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var docFilter *RequestedDocumentRestrictionsFilter + docFilter, err = NewRequestedDocumentRestrictionsFilterBuilder(). + ForIncludeList(). + WithDocumentRestriction(docRestriction). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err = NewRequiredSupplementaryDocumentBuilder(). + WithFilter(docFilter). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT","filter":{"type":"DOCUMENT_RESTRICTIONS","inclusion":"WHITELIST","documents":[{"document_types":["UTILITY_BILL"]}]}} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithCountryCodes() { + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err := NewRequiredSupplementaryDocumentBuilder(). + WithCountryCodes([]string{"SOME_COUNTRY"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT","country_codes":["SOME_COUNTRY"]} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithCountryCodes_empty() { + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err := NewRequiredSupplementaryDocumentBuilder(). + WithCountryCodes([]string{}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT"} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithDocumentTypes() { + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err := NewRequiredSupplementaryDocumentBuilder(). + WithDocumentTypes([]string{"SOME_DOCUMENT_TYPE"}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT","document_types":["SOME_DOCUMENT_TYPE"]} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithDocumentTypes_empty() { + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err := NewRequiredSupplementaryDocumentBuilder(). + WithDocumentTypes([]string{}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT"} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithObjective() { + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err := NewRequiredSupplementaryDocumentBuilder(). + WithObjective(&mockObjective{}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT","objective":{"type":"SOME_OBJECTIVE"}} +} + +func ExampleRequiredSupplementaryDocumentBuilder_WithObjective_proofOfAddress() { + var proofOfAddress objective.Objective + proofOfAddress, err := objective.NewProofOfAddressObjectiveBuilder().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + var requiredSupplementaryDocument *RequiredSupplementaryDocument + requiredSupplementaryDocument, err = NewRequiredSupplementaryDocumentBuilder(). + WithObjective(proofOfAddress). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(requiredSupplementaryDocument) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT","objective":{"type":"PROOF_OF_ADDRESS"}} +} + +type mockObjective struct{} + +func (o *mockObjective) Type() string { + return "SOME_OBJECTIVE" +} + +// MarshalJSON returns the JSON encoding +func (o *mockObjective) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + }{ + Type: o.Type(), + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/restriction.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/restriction.go new file mode 100644 index 0000000..7709f16 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/filter/restriction.go @@ -0,0 +1,13 @@ +package filter + +// TypeRestriction is a restriction of the type of document required +type TypeRestriction struct { + Inclusion string `json:"inclusion"` + DocumentTypes []string `json:"document_types"` +} + +// CountryRestriction is a restriction of the country in which a document pertains to +type CountryRestriction struct { + Inclusion string `json:"inclusion"` + CountryCodes []string `json:"country_codes"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token.go new file mode 100644 index 0000000..33ccb42 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token.go @@ -0,0 +1,35 @@ +package create + +import "time" + +type ImportToken struct { + Ttl int `json:"ttl"` +} + +// defaultImportTokenTTL is 12 Months +const defaultImportTokenTTL = time.Hour * 24 * 365 + +// NewImportTokenBuilder creates a new ImportTokenBuilder +func NewImportTokenBuilder() *ImportTokenBuilder { + return &ImportTokenBuilder{ + Ttl: int(defaultImportTokenTTL.Seconds()), + } +} + +// ImportTokenBuilder builds the ImportToken struct +type ImportTokenBuilder struct { + Ttl int +} + +// WithTTL sets the TTL of the import-token (in seconds) +func (b *ImportTokenBuilder) WithTTL(ttl int) *ImportTokenBuilder { + b.Ttl = ttl + return b +} + +// Build builds the ImportToken struct using the supplied values +func (b *ImportTokenBuilder) Build() (*ImportToken, error) { + return &ImportToken{ + Ttl: b.Ttl, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token_test.go new file mode 100644 index 0000000..fdf3384 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/import_token_test.go @@ -0,0 +1,28 @@ +package create + +import ( + "encoding/json" + "fmt" + "time" +) + +func ExampleImportTokenBuilder_Build() { + ttl := time.Hour * 24 * 30 + it, err := NewImportTokenBuilder(). + WithTTL(int(ttl.Seconds())). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(it) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"ttl":2592000} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config.go new file mode 100644 index 0000000..9391a1f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config.go @@ -0,0 +1,75 @@ +package create + +import "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + +// NotificationConfig represents the configuration properties for notifications within the Doc Scan (IDV) system. +// Notifications can be configured within a Doc Scan (IDV) session to allow your backend to be +// notified of certain events, without having to constantly poll for the state of a session. +type NotificationConfig struct { + AuthToken string `json:"auth_token,omitempty"` + Endpoint string `json:"endpoint,omitempty"` + Topics []string `json:"topics,omitempty"` +} + +// NewNotificationConfigBuilder creates a new NotificationConfigBuilder +func NewNotificationConfigBuilder() *NotificationConfigBuilder { + return &NotificationConfigBuilder{} +} + +// NotificationConfigBuilder builds the NotificationConfig struct +type NotificationConfigBuilder struct { + authToken string + endpoint string + topics []string +} + +// WithAuthToken sets the authorization token to be included in call-back messages +func (b *NotificationConfigBuilder) WithAuthToken(token string) *NotificationConfigBuilder { + b.authToken = token + return b +} + +// WithEndpoint sets the endpoint that notifications should be sent to +func (b *NotificationConfigBuilder) WithEndpoint(endpoint string) *NotificationConfigBuilder { + b.endpoint = endpoint + return b +} + +// WithTopic adds a topic to the slice of topics that trigger notification messages +func (b *NotificationConfigBuilder) WithTopic(topic string) *NotificationConfigBuilder { + b.topics = append(b.topics, topic) + return b +} + +// ForResourceUpdate Adds "RESOURCE_UPDATE" to the slice of topics that trigger notification messages +func (b *NotificationConfigBuilder) ForResourceUpdate() *NotificationConfigBuilder { + b.topics = append(b.topics, constants.ResourceUpdate) + return b +} + +// ForTaskCompletion Adds "TASK_COMPLETION" to the slice of topics that trigger notification messages +func (b *NotificationConfigBuilder) ForTaskCompletion() *NotificationConfigBuilder { + b.topics = append(b.topics, constants.TaskCompletion) + return b +} + +// ForSessionCompletion Adds "SESSION_COMPLETION" to the slice of topics that trigger notification messages +func (b *NotificationConfigBuilder) ForSessionCompletion() *NotificationConfigBuilder { + b.topics = append(b.topics, constants.SessionCompletion) + return b +} + +// ForCheckCompletion Adds "CHECK_COMPLETION" to the slice of topics that trigger notification messages +func (b *NotificationConfigBuilder) ForCheckCompletion() *NotificationConfigBuilder { + b.topics = append(b.topics, constants.CheckCompletion) + return b +} + +// Build builds the NotificationConfig struct using the supplied values +func (b *NotificationConfigBuilder) Build() (*NotificationConfig, error) { + return &NotificationConfig{ + b.authToken, + b.endpoint, + b.topics, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config_test.go new file mode 100644 index 0000000..ce4a214 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/notification_config_test.go @@ -0,0 +1,32 @@ +package create + +import ( + "encoding/json" + "fmt" +) + +func ExampleNotificationConfigBuilder_Build() { + notifications, err := NewNotificationConfigBuilder(). + WithAuthToken("auth-token"). + WithEndpoint("/endpoint"). + WithTopic("SOME_TOPIC"). + ForCheckCompletion(). + ForResourceUpdate(). + ForSessionCompletion(). + ForTaskCompletion(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(notifications) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"auth_token":"auth-token","endpoint":"/endpoint","topics":["SOME_TOPIC","CHECK_COMPLETION","RESOURCE_UPDATE","SESSION_COMPLETION","TASK_COMPLETION"]} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/objective.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/objective.go new file mode 100644 index 0000000..31c4f5e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/objective.go @@ -0,0 +1,6 @@ +package objective + +type Objective interface { + Type() string + MarshalJSON() ([]byte, error) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address.go new file mode 100644 index 0000000..5374bb8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address.go @@ -0,0 +1,39 @@ +package objective + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// ProofOfAddressObjective requests creation of a proof of address objective +type ProofOfAddressObjective struct { +} + +// Type is the objective type +func (o *ProofOfAddressObjective) Type() string { + return constants.ProofOfAddress +} + +// MarshalJSON returns the JSON encoding +func (o *ProofOfAddressObjective) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + }{ + Type: o.Type(), + }) +} + +// NewProofOfAddressObjectiveBuilder creates a new ProofOfAddressObjectiveBuilder +func NewProofOfAddressObjectiveBuilder() *ProofOfAddressObjectiveBuilder { + return &ProofOfAddressObjectiveBuilder{} +} + +// ProofOfAddressObjectiveBuilder builds a ProofOfAddress +type ProofOfAddressObjectiveBuilder struct { +} + +// Build builds the ProofOfAddressObjective +func (builder *ProofOfAddressObjectiveBuilder) Build() (*ProofOfAddressObjective, error) { + return &ProofOfAddressObjective{}, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address_test.go new file mode 100644 index 0000000..3456c5d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/objective/proof_of_address_test.go @@ -0,0 +1,24 @@ +package objective + +import ( + "encoding/json" + "fmt" +) + +func ExampleProofOfAddressObjectiveBuilder() { + objective, err := NewProofOfAddressObjectiveBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(objective) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"PROOF_OF_ADDRESS"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config.go new file mode 100644 index 0000000..fbbe596 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config.go @@ -0,0 +1,206 @@ +package create + +import "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + +// SDKConfig provides configuration properties for the the web/native clients +type SDKConfig struct { + AllowedCaptureMethods string `json:"allowed_capture_methods,omitempty"` + PrimaryColour string `json:"primary_colour,omitempty"` + SecondaryColour string `json:"secondary_colour,omitempty"` + FontColour string `json:"font_colour,omitempty"` + Locale string `json:"locale,omitempty"` + PresetIssuingCountry string `json:"preset_issuing_country,omitempty"` + SuccessUrl string `json:"success_url,omitempty"` + ErrorUrl string `json:"error_url,omitempty"` + PrivacyPolicyUrl string `json:"privacy_policy_url,omitempty"` + AttemptsConfiguration *AttemptsConfiguration `json:"attempts_configuration,omitempty"` + AllowHandOff bool `json:"allow_handoff,omitempty"` + DarkMode string `json:"dark_mode,omitempty"` + PrimaryColourDarkMode string `json:"primary_colour_dark_mode,omitempty"` + BiometricConsentFlow string `json:"biometric_consent_flow,omitempty"` + BrandId string `json:"brand_id,omitempty"` +} + +type AttemptsConfiguration struct { + IdDocumentTextDataExtraction map[string]int `json:"ID_DOCUMENT_TEXT_DATA_EXTRACTION,omitempty"` +} + +// NewSdkConfigBuilder creates a new SdkConfigBuilder +func NewSdkConfigBuilder() *SdkConfigBuilder { + return &SdkConfigBuilder{} +} + +// SdkConfigBuilder builds the SDKConfig struct +type SdkConfigBuilder struct { + allowedCaptureMethods string + primaryColour string + secondaryColour string + fontColour string + locale string + presetIssuingCountry string + successUrl string + errorUrl string + privacyPolicyUrl string + idDocumentTextDataExtractionAttempts map[string]int + allowHandOff bool + darkMode string + primaryColourDarkMode string + biometricConsentFlow string + brandId string +} + +// WithAllowedCaptureMethods sets the allowed capture methods on the builder +func (b *SdkConfigBuilder) WithAllowedCaptureMethods(captureMethods string) *SdkConfigBuilder { + b.allowedCaptureMethods = captureMethods + return b +} + +// WithAllowsCamera sets the allowed capture method to "CAMERA" +func (b *SdkConfigBuilder) WithAllowsCamera() *SdkConfigBuilder { + return b.WithAllowedCaptureMethods(constants.Camera) +} + +// WithAllowsCameraAndUpload sets the allowed capture method to "CAMERA_AND_UPLOAD" +func (b *SdkConfigBuilder) WithAllowsCameraAndUpload() *SdkConfigBuilder { + return b.WithAllowedCaptureMethods(constants.CameraAndUpload) +} + +// WithPrimaryColour sets the primary colour to be used by the web/native client, hexadecimal value e.g. #ff0000 +func (b *SdkConfigBuilder) WithPrimaryColour(colour string) *SdkConfigBuilder { + b.primaryColour = colour + return b +} + +// WithSecondaryColour sets the secondary colour to be used by the web/native client (used on the button), hexadecimal value e.g. #ff0000 +func (b *SdkConfigBuilder) WithSecondaryColour(colour string) *SdkConfigBuilder { + b.secondaryColour = colour + return b +} + +// WithFontColour the font colour to be used by the web/native client (used on the button), hexadecimal value e.g. #ff0000 +func (b *SdkConfigBuilder) WithFontColour(colour string) *SdkConfigBuilder { + b.fontColour = colour + return b +} + +// WithLocale sets the language locale use by the web/native client +func (b *SdkConfigBuilder) WithLocale(locale string) *SdkConfigBuilder { + b.locale = locale + return b +} + +// WithPresetIssuingCountry sets the preset issuing country used by the web/native client +func (b *SdkConfigBuilder) WithPresetIssuingCountry(country string) *SdkConfigBuilder { + b.presetIssuingCountry = country + return b +} + +// WithSuccessUrl sets the success URL for the redirect that follows the web/native client uploading documents successfully +func (b *SdkConfigBuilder) WithSuccessUrl(url string) *SdkConfigBuilder { + b.successUrl = url + return b +} + +// WithErrorUrl sets the error URL for the redirect that follows the web/native client uploading documents unsuccessfully +func (b *SdkConfigBuilder) WithErrorUrl(url string) *SdkConfigBuilder { + b.errorUrl = url + return b +} + +// WithPrivacyPolicyUrl sets the privacy policy URL +func (b *SdkConfigBuilder) WithPrivacyPolicyUrl(url string) *SdkConfigBuilder { + b.privacyPolicyUrl = url + return b +} + +func (b *SdkConfigBuilder) WithIdDocumentTextExtractionCategoryAttempts(category string, attempts int) *SdkConfigBuilder { + if b.idDocumentTextDataExtractionAttempts == nil { + b.idDocumentTextDataExtractionAttempts = make(map[string]int) + } + b.idDocumentTextDataExtractionAttempts[category] = attempts + return b +} + +func (b *SdkConfigBuilder) WithIdDocumentTextExtractionReclassificationAttempts(attempts int) *SdkConfigBuilder { + return b.WithIdDocumentTextExtractionCategoryAttempts(reclassification, attempts) +} + +func (b *SdkConfigBuilder) WithIdDocumentTextExtractionGenericAttempts(attempts int) *SdkConfigBuilder { + return b.WithIdDocumentTextExtractionCategoryAttempts(generic, attempts) +} + +func (b *SdkConfigBuilder) WithAllowHandOff(allowHandOff bool) *SdkConfigBuilder { + b.allowHandOff = allowHandOff + return b +} + +func (b *SdkConfigBuilder) WithDarkMode(darkMode string) *SdkConfigBuilder { + b.darkMode = darkMode + return b +} + +func (b *SdkConfigBuilder) WithDarkModeOn() *SdkConfigBuilder { + b.darkMode = "ON" + return b +} + +func (b *SdkConfigBuilder) WithDarkModeOff() *SdkConfigBuilder { + b.darkMode = "OFF" + return b +} + +func (b *SdkConfigBuilder) WithDarkModeAuto() *SdkConfigBuilder { + b.darkMode = "AUTO" + return b +} + +func (b *SdkConfigBuilder) WithPrimaryColourDarkMode(colour string) *SdkConfigBuilder { + b.primaryColourDarkMode = colour + return b +} + +func (b *SdkConfigBuilder) WithBiometricConsentFlow(biometricConsentFlow string) *SdkConfigBuilder { + b.biometricConsentFlow = biometricConsentFlow + return b +} + +func (b *SdkConfigBuilder) WithEarlyBiometricConsentFlow() *SdkConfigBuilder { + return b.WithBiometricConsentFlow(constants.Early) +} + +func (b *SdkConfigBuilder) WithJustInTimeBiometricConsentFlow() *SdkConfigBuilder { + return b.WithBiometricConsentFlow(constants.JustInTime) +} + +func (b *SdkConfigBuilder) WithBrandId(brandId string) *SdkConfigBuilder { + b.brandId = brandId + return b +} + +// Build builds the SDKConfig struct using the supplied values +func (b *SdkConfigBuilder) Build() (*SDKConfig, error) { + sdkConf := &SDKConfig{ + b.allowedCaptureMethods, + b.primaryColour, + b.secondaryColour, + b.fontColour, + b.locale, + b.presetIssuingCountry, + b.successUrl, + b.errorUrl, + b.privacyPolicyUrl, + nil, + b.allowHandOff, + b.darkMode, + b.primaryColourDarkMode, + b.biometricConsentFlow, + b.brandId, + } + + if b.idDocumentTextDataExtractionAttempts != nil { + sdkConf.AttemptsConfiguration = &AttemptsConfiguration{ + IdDocumentTextDataExtraction: b.idDocumentTextDataExtractionAttempts, + } + } + return sdkConf, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config_test.go new file mode 100644 index 0000000..b917290 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/sdk_config_test.go @@ -0,0 +1,259 @@ +package create + +import ( + "encoding/json" + "fmt" +) + +func ExampleSdkConfigBuilder_Build() { + sdkConfig, err := NewSdkConfigBuilder(). + WithAllowsCamera(). + WithErrorUrl("https://example.com/error"). + WithFontColour("#ff0000"). + WithLocale("fr_FR"). + WithPresetIssuingCountry("USA"). + WithPrimaryColour("#aa1111"). + WithSecondaryColour("#bb2222"). + WithSuccessUrl("https://example.com/success"). + WithPrivacyPolicyUrl("https://example.com/privacy"). + WithIdDocumentTextExtractionCategoryAttempts("test_category", 3). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"allowed_capture_methods":"CAMERA","primary_colour":"#aa1111","secondary_colour":"#bb2222","font_colour":"#ff0000","locale":"fr_FR","preset_issuing_country":"USA","success_url":"https://example.com/success","error_url":"https://example.com/error","privacy_policy_url":"https://example.com/privacy","attempts_configuration":{"ID_DOCUMENT_TEXT_DATA_EXTRACTION":{"test_category":3}}} +} + +func ExampleSdkConfigBuilder_Build_repeatedCallWithIdDocumentTextExtractionCategoryAttempts() { + sdkConfig, err := NewSdkConfigBuilder(). + WithIdDocumentTextExtractionCategoryAttempts("test_category", 3). + WithIdDocumentTextExtractionCategoryAttempts("test_category", 2). + WithIdDocumentTextExtractionCategoryAttempts("test_category", 1). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"attempts_configuration":{"ID_DOCUMENT_TEXT_DATA_EXTRACTION":{"test_category":1}}} +} + +func ExampleSdkConfigBuilder_Build_multipleCategoriesWithIdDocumentTextExtractionCategoryAttempts() { + sdkConfig, err := NewSdkConfigBuilder(). + WithIdDocumentTextExtractionGenericAttempts(3). + WithIdDocumentTextExtractionCategoryAttempts("test_category", 2). + WithIdDocumentTextExtractionReclassificationAttempts(1). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"attempts_configuration":{"ID_DOCUMENT_TEXT_DATA_EXTRACTION":{"GENERIC":3,"RECLASSIFICATION":1,"test_category":2}}} +} + +func ExampleSdkConfigBuilder_WithAllowsCameraAndUpload() { + sdkConfig, err := NewSdkConfigBuilder(). + WithAllowsCameraAndUpload(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"allowed_capture_methods":"CAMERA_AND_UPLOAD"} +} + +func ExampleSdkConfigBuilder_WithEarlyBiometricConsentFlow() { + sdkConfig, err := NewSdkConfigBuilder(). + WithEarlyBiometricConsentFlow(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"biometric_consent_flow":"EARLY"} +} + +func ExampleSdkConfigBuilder_WithJustInTimeBiometricConsentFlow() { + sdkConfig, err := NewSdkConfigBuilder(). + WithJustInTimeBiometricConsentFlow(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"biometric_consent_flow":"JUST_IN_TIME"} +} + +func ExampleSdkConfigBuilder_WithAllowHandOff() { + sdkConfig, err := NewSdkConfigBuilder(). + WithAllowHandOff(true). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"allow_handoff":true} +} + +func ExampleSdkConfigBuilder_WithDarkMode() { + sdkConfig, err := NewSdkConfigBuilder(). + WithDarkMode("ON"). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"dark_mode":"ON"} +} + +func ExampleSdkConfigBuilder_WithBrandId() { + sdkConfig, err := NewSdkConfigBuilder(). + WithBrandId("some_brand_id"). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"brand_id":"some_brand_id"} +} + +func ExampleSdkConfigBuilder_WithDarkModeOff() { + sdkConfig, err := NewSdkConfigBuilder(). + WithDarkModeOff(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"dark_mode":"OFF"} +} + +func ExampleSdkConfigBuilder_WithDarkModeAuto() { + sdkConfig, err := NewSdkConfigBuilder(). + WithDarkModeAuto(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"dark_mode":"AUTO"} +} + +func ExampleSdkConfigBuilder_WithPrimaryColourDarkMode() { + sdkConfig, err := NewSdkConfigBuilder(). + WithPrimaryColourDarkMode("SOME_COLOUR"). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sdkConfig) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"primary_colour_dark_mode":"SOME_COLOUR"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec.go new file mode 100644 index 0000000..10fc148 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec.go @@ -0,0 +1,195 @@ +package create + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/check" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/filter" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/task" +) + +// SessionSpecification is the definition for the Doc Scan (IDV) Session to be created +type SessionSpecification struct { + // ClientSessionTokenTTL Client-session-token time-to-live to apply to the created Session + ClientSessionTokenTTL int `json:"client_session_token_ttl,omitempty"` + + // ResourcesTTL time-to-live used for all Resources created in the course of the session + ResourcesTTL int `json:"resources_ttl,omitempty"` + + // UserTrackingID the User tracking ID, used to track returning users + UserTrackingID string `json:"user_tracking_id,omitempty"` + + // Notifications for configuring call-back messages + Notifications *NotificationConfig `json:"notifications,omitempty"` + + // RequestedChecks is a slice of check.RequestedCheck objects defining the Checks to be performed on each Document + RequestedChecks []check.RequestedCheck `json:"requested_checks,omitempty"` + + // RequestedTasks is a slice of task.RequestedTask objects defining the Tasks to be performed on each Document + RequestedTasks []task.RequestedTask `json:"requested_tasks,omitempty"` + + // SdkConfig retrieves the SDK configuration set of the session specification + SdkConfig *SDKConfig `json:"sdk_config,omitempty"` + + // RequiredDocuments is a slice of documents that are required from the user to satisfy a sessions requirements. + RequiredDocuments []filter.RequiredDocument `json:"required_documents,omitempty"` + + // BlockBiometricConsent sets whether or not to block the collection of biometric consent + BlockBiometricConsent *bool `json:"block_biometric_consent,omitempty"` + + // IdentityProfileRequirements is a JSON object for defining a required identity profile + // within the scope of a trust framework and scheme. + IdentityProfileRequirements *json.RawMessage `json:"identity_profile_requirements,omitempty"` + + // AdvancedIdentityProfileRequirements is a JSON object for defining a required advanced identity profile + // within the scope of specified trust frameworks and schemes. + AdvancedIdentityProfileRequirements *json.RawMessage `json:"advanced_identity_profile_requirements,omitempty"` + + // CreateIdentityProfilePreview is a bool for enabling the creation of the IdentityProfilePreview + CreateIdentityProfilePreview bool `json:"create_identity_profile_preview,omitempty"` + + // Subject provides information on the subject allowing to track the same user across multiple sessions. + // Should not contain any personal identifiable information. + Subject *json.RawMessage `json:"subject,omitempty"` + + // ImportToken requests the creation of an import_token. + ImportToken *ImportToken `json:"import_token,omitempty"` + + //Ephemeral Media to set ephemeral or not + EphemeralMedia *bool `json:"ephemeral_media,omitempty"` +} + +// SessionSpecificationBuilder builds the SessionSpecification struct +type SessionSpecificationBuilder struct { + clientSessionTokenTTL int + resourcesTTL int + userTrackingID string + notifications *NotificationConfig + requestedChecks []check.RequestedCheck + requestedTasks []task.RequestedTask + sdkConfig *SDKConfig + requiredDocuments []filter.RequiredDocument + blockBiometricConsent *bool + identityProfileRequirements *json.RawMessage + advancedIdentityProfileRequirements *json.RawMessage + createIdentityProfilePreview bool + subject *json.RawMessage + importToken *ImportToken + ephemeralMedia *bool +} + +// NewSessionSpecificationBuilder creates a new SessionSpecificationBuilder +func NewSessionSpecificationBuilder() *SessionSpecificationBuilder { + return &SessionSpecificationBuilder{} +} + +// WithClientSessionTokenTTL sets the client session token TTL (time-to-live) +func (b *SessionSpecificationBuilder) WithClientSessionTokenTTL(clientSessionTokenTTL int) *SessionSpecificationBuilder { + b.clientSessionTokenTTL = clientSessionTokenTTL + return b +} + +// WithResourcesTTL sets the client session token TTL (time-to-live) +func (b *SessionSpecificationBuilder) WithResourcesTTL(resourcesTTL int) *SessionSpecificationBuilder { + b.resourcesTTL = resourcesTTL + return b +} + +// WithUserTrackingID sets the user tracking ID +func (b *SessionSpecificationBuilder) WithUserTrackingID(userTrackingID string) *SessionSpecificationBuilder { + b.userTrackingID = userTrackingID + return b +} + +// WithNotifications sets the NotificationConfig +func (b *SessionSpecificationBuilder) WithNotifications(notificationConfig *NotificationConfig) *SessionSpecificationBuilder { + b.notifications = notificationConfig + return b +} + +// WithRequestedCheck adds a RequestedCheck to the required checks +func (b *SessionSpecificationBuilder) WithRequestedCheck(requestedCheck check.RequestedCheck) *SessionSpecificationBuilder { + b.requestedChecks = append(b.requestedChecks, requestedCheck) + return b +} + +// WithRequestedTask adds a RequestedTask to the required tasks +func (b *SessionSpecificationBuilder) WithRequestedTask(requestedTask task.RequestedTask) *SessionSpecificationBuilder { + b.requestedTasks = append(b.requestedTasks, requestedTask) + return b +} + +// WithSDKConfig sets the SDKConfig +func (b *SessionSpecificationBuilder) WithSDKConfig(SDKConfig *SDKConfig) *SessionSpecificationBuilder { + b.sdkConfig = SDKConfig + return b +} + +// WithRequiredDocument adds a required document to the session specification +func (b *SessionSpecificationBuilder) WithRequiredDocument(document filter.RequiredDocument) *SessionSpecificationBuilder { + b.requiredDocuments = append(b.requiredDocuments, document) + return b +} + +// WithBlockBiometricConsent sets whether or not to block the collection of biometric consent +func (b *SessionSpecificationBuilder) WithBlockBiometricConsent(blockBiometricConsent bool) *SessionSpecificationBuilder { + b.blockBiometricConsent = &blockBiometricConsent + return b +} + +// WithCreateIdentityProfilePreview sets whether or not an Identity Profile Preview will be created. +func (b *SessionSpecificationBuilder) WithCreateIdentityProfilePreview(createIdentityProfilePreview bool) *SessionSpecificationBuilder { + b.createIdentityProfilePreview = createIdentityProfilePreview + return b +} + +// WithIdentityProfileRequirements adds Identity Profile Requirements to the session. Must be valid JSON. +func (b *SessionSpecificationBuilder) WithIdentityProfileRequirements(identityProfile json.RawMessage) *SessionSpecificationBuilder { + b.identityProfileRequirements = &identityProfile + return b +} + +// WithAdvancedIdentityProfileRequirements adds Advanced Identity Profile Requirements to the session. Must be valid JSON. +func (b *SessionSpecificationBuilder) WithAdvancedIdentityProfileRequirements(advancedIdentityProfile json.RawMessage) *SessionSpecificationBuilder { + b.advancedIdentityProfileRequirements = &advancedIdentityProfile + return b +} + +// WithSubject adds Subject to the session. Must be valid JSON. +func (b *SessionSpecificationBuilder) WithSubject(subject json.RawMessage) *SessionSpecificationBuilder { + b.subject = &subject + return b +} + +// WithImportToken sets whether an ImportToken is to be generated. +func (b *SessionSpecificationBuilder) WithImportToken(importToken *ImportToken) *SessionSpecificationBuilder { + b.importToken = importToken + return b +} + +// WithEphemeralMedia sets whether to block or not to block the collection of ephemeral media +func (b *SessionSpecificationBuilder) WithEphemeralMedia(ephemeralMedia bool) *SessionSpecificationBuilder { + b.ephemeralMedia = &ephemeralMedia + return b +} + +// Build builds the SessionSpecification struct +func (b *SessionSpecificationBuilder) Build() (*SessionSpecification, error) { + return &SessionSpecification{ + b.clientSessionTokenTTL, + b.resourcesTTL, + b.userTrackingID, + b.notifications, + b.requestedChecks, + b.requestedTasks, + b.sdkConfig, + b.requiredDocuments, + b.blockBiometricConsent, + b.identityProfileRequirements, + b.advancedIdentityProfileRequirements, + b.createIdentityProfilePreview, + b.subject, + b.importToken, + b.ephemeralMedia, + }, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec_test.go new file mode 100644 index 0000000..235b2dc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/session_spec_test.go @@ -0,0 +1,385 @@ +package create + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/check" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/filter" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/create/task" +) + +func ExampleSessionSpecificationBuilder_Build() { + notifications, err := NewNotificationConfigBuilder(). + WithTopic("some-topic"). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + faceMatchCheck, err := check.NewRequestedFaceMatchCheckBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + documentAuthenticityCheck, err := check.NewRequestedDocumentAuthenticityCheckBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + livenessCheck, err := check.NewRequestedLivenessCheckBuilder(). + ForLivenessType("LIVENESSTYPE"). + WithMaxRetries(5). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + textExtractionTask, err := task.NewRequestedTextExtractionTaskBuilder(). + WithManualCheckFallback(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + sdkConfig, err := NewSdkConfigBuilder(). + WithAllowsCamera(). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + requiredIDDocument, err := filter.NewRequiredIDDocumentBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithClientSessionTokenTTL(789). + WithResourcesTTL(456). + WithUserTrackingID("some-tracking-id"). + WithNotifications(notifications). + WithRequestedCheck(faceMatchCheck). + WithRequestedCheck(documentAuthenticityCheck). + WithRequestedCheck(livenessCheck). + WithRequestedTask(textExtractionTask). + WithSDKConfig(sdkConfig). + WithRequiredDocument(requiredIDDocument). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"client_session_token_ttl":789,"resources_ttl":456,"user_tracking_id":"some-tracking-id","notifications":{"topics":["some-topic"]},"requested_checks":[{"type":"ID_DOCUMENT_FACE_MATCH","config":{"manual_check":"NEVER"}},{"type":"ID_DOCUMENT_AUTHENTICITY","config":{}},{"type":"LIVENESS","config":{"max_retries":5,"liveness_type":"LIVENESSTYPE"}}],"requested_tasks":[{"type":"ID_DOCUMENT_TEXT_DATA_EXTRACTION","config":{"manual_check":"FALLBACK"}}],"sdk_config":{"allowed_capture_methods":"CAMERA"},"required_documents":[{"type":"ID_DOCUMENT"}]} +} + +func ExampleSessionSpecificationBuilder_Build_withBlockBiometricConsentTrue() { + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithBlockBiometricConsent(true). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"block_biometric_consent":true} +} + +func ExampleSessionSpecificationBuilder_Build_withBlockBiometricConsentFalse() { + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithBlockBiometricConsent(false). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"block_biometric_consent":false} +} + +func ExampleSessionSpecificationBuilder_WithRequiredDocument_supplementary() { + requiredSupplementaryDocument, err := filter.NewRequiredSupplementaryDocumentBuilder(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithRequiredDocument(requiredSupplementaryDocument). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"required_documents":[{"type":"SUPPLEMENTARY_DOCUMENT"}]} +} + +func ExampleSessionSpecificationBuilder_Build_withIdentityProfileRequirements() { + identityProfile := []byte(`{ + "trust_framework": "UK_TFIDA", + "scheme": { + "type": "DBS", + "objective": "STANDARD" + } + }`) + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithIdentityProfileRequirements(identityProfile). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"identity_profile_requirements":{"trust_framework":"UK_TFIDA","scheme":{"type":"DBS","objective":"STANDARD"}}} +} + +func TestExampleSessionSpecificationBuilder_Build_WithIdentityProfileRequirements_InvalidJSON(t *testing.T) { + identityProfile := []byte(`{ + "trust_framework": UK_TFIDA", + , + }`) + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithIdentityProfileRequirements(identityProfile). + Build() + + if err != nil { + t.Errorf("error: %s", err.Error()) + return + } + + _, err = json.Marshal(sessionSpecification) + if err == nil { + t.Error("expected an error") + return + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} + +func ExampleSessionSpecificationBuilder_Build_withAdvancedIdentityProfileRequirements() { + advancedIdentityProfile := []byte(`{ + "profiles": [ + { + "trust_framework": "UK_TFIDA", + "schemes": [ + { + "label": "LB912", + "type": "RTW" + }, + { + "label": "LB777", + "type": "DBS", + "objective": "BASIC" + } + ] + }, + { + "trust_framework": "YOTI_GLOBAL", + "schemes": [ + { + "label": "LB321", + "type": "IDENTITY", + "objective": "AL_L1", + "config": {} + } + ] + } + ] + }`) + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithAdvancedIdentityProfileRequirements(advancedIdentityProfile). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"advanced_identity_profile_requirements":{"profiles":[{"trust_framework":"UK_TFIDA","schemes":[{"label":"LB912","type":"RTW"},{"label":"LB777","type":"DBS","objective":"BASIC"}]},{"trust_framework":"YOTI_GLOBAL","schemes":[{"label":"LB321","type":"IDENTITY","objective":"AL_L1","config":{}}]}]}} +} + +func TestExampleSessionSpecificationBuilder_Build_WithAdvancedIdentityProfileRequirements_InvalidJSON(t *testing.T) { + advancedIdentityProfile := []byte(`{ + "trust_framework": UK_TFIDA", + , + }`) + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithAdvancedIdentityProfileRequirements(advancedIdentityProfile). + Build() + + if err != nil { + t.Errorf("error: %s", err.Error()) + return + } + + _, err = json.Marshal(sessionSpecification) + if err == nil { + t.Error("expected an error") + return + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} + +func ExampleSessionSpecificationBuilder_Build_withSubject() { + subject := []byte(`{ + "subject_id": "Original subject ID" + }`) + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithSubject(subject). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"subject":{"subject_id":"Original subject ID"}} +} + +func TestExampleSessionSpecificationBuilder_Build_WithSubject_InvalidJSON(t *testing.T) { + subject := []byte(`{ + "Original subject ID" + }`) + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithSubject(subject). + Build() + + if err != nil { + t.Errorf("error: %s", err.Error()) + return + } + + _, err = json.Marshal(sessionSpecification) + if err == nil { + t.Error("expected an error") + return + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} + +func ExampleSessionSpecificationBuilder_Build_withCreateIdentityProfilePreview() { + + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithCreateIdentityProfilePreview(true). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"create_identity_profile_preview":true} +} + +func ExampleSessionSpecificationBuilder_Build_withEphemeralMedia() { + sessionSpecification, err := NewSessionSpecificationBuilder(). + WithEphemeralMedia(true). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(sessionSpecification) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"ephemeral_media":true} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/constants.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/constants.go new file mode 100644 index 0000000..901d8ad --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/constants.go @@ -0,0 +1,6 @@ +package task + +const ( + chipDataDesired string = "DESIRED" + chipDataIgnore string = "IGNORE" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/requested_task.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/requested_task.go new file mode 100644 index 0000000..100f448 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/requested_task.go @@ -0,0 +1,12 @@ +package task + +// RequestedTask requests creation of a Task to be performed on each document +type RequestedTask interface { + Type() string + Config() RequestedTaskConfig + MarshalJSON() ([]byte, error) +} + +// RequestedTaskConfig is the configuration applied when creating a Task +type RequestedTaskConfig interface { +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction.go new file mode 100644 index 0000000..7a3b626 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction.go @@ -0,0 +1,80 @@ +package task + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedSupplementaryDocTextExtractionTask requests creation of a Text Extraction Task +type RequestedSupplementaryDocTextExtractionTask struct { + config RequestedSupplementaryDocTextExtractionTaskConfig +} + +// Type is the type of the Requested Task +func (t *RequestedSupplementaryDocTextExtractionTask) Type() string { + return constants.SupplementaryDocumentTextDataExtraction +} + +// Config is the configuration of the Requested Task +func (t *RequestedSupplementaryDocTextExtractionTask) Config() RequestedTaskConfig { + return t.config +} + +// MarshalJSON marshals the RequestedSupplementaryDocTextExtractionTask to JSON +func (t *RequestedSupplementaryDocTextExtractionTask) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedTaskConfig `json:"config,omitempty"` + }{ + Type: t.Type(), + Config: t.Config(), + }) +} + +// NewRequestedSupplementaryDocTextExtractionTask creates a new supplementary document text extraction task +func NewRequestedSupplementaryDocTextExtractionTask(config RequestedSupplementaryDocTextExtractionTaskConfig) *RequestedSupplementaryDocTextExtractionTask { + return &RequestedSupplementaryDocTextExtractionTask{config} +} + +// RequestedSupplementaryDocTextExtractionTaskConfig is the configuration applied when creating a Text Extraction Task +type RequestedSupplementaryDocTextExtractionTaskConfig struct { + ManualCheck string `json:"manual_check,omitempty"` +} + +// NewRequestedSupplementaryDocTextExtractionTaskBuilder creates a new RequestedSupplementaryDocTextExtractionTaskBuilder +func NewRequestedSupplementaryDocTextExtractionTaskBuilder() *RequestedSupplementaryDocTextExtractionTaskBuilder { + return &RequestedSupplementaryDocTextExtractionTaskBuilder{} +} + +// RequestedSupplementaryDocTextExtractionTaskBuilder builds a RequestedSupplementaryDocTextExtractionTask +type RequestedSupplementaryDocTextExtractionTaskBuilder struct { + manualCheck string +} + +// WithManualCheckAlways sets the value of manual check to "ALWAYS" +func (builder *RequestedSupplementaryDocTextExtractionTaskBuilder) WithManualCheckAlways() *RequestedSupplementaryDocTextExtractionTaskBuilder { + builder.manualCheck = constants.Always + return builder +} + +// WithManualCheckFallback sets the value of manual check to "FALLBACK" +func (builder *RequestedSupplementaryDocTextExtractionTaskBuilder) WithManualCheckFallback() *RequestedSupplementaryDocTextExtractionTaskBuilder { + builder.manualCheck = constants.Fallback + return builder +} + +// WithManualCheckNever sets the value of manual check to "NEVER" +func (builder *RequestedSupplementaryDocTextExtractionTaskBuilder) WithManualCheckNever() *RequestedSupplementaryDocTextExtractionTaskBuilder { + builder.manualCheck = constants.Never + return builder +} + +// Build builds the RequestedSupplementaryDocTextExtractionTask +func (builder *RequestedSupplementaryDocTextExtractionTaskBuilder) Build() (*RequestedSupplementaryDocTextExtractionTask, error) { + config := RequestedSupplementaryDocTextExtractionTaskConfig{ + builder.manualCheck, + } + + return NewRequestedSupplementaryDocTextExtractionTask(config), nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction_test.go new file mode 100644 index 0000000..77c12d6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/supplementary_text_extraction_test.go @@ -0,0 +1,78 @@ +package task + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleRequestedSupplementaryDocTextExtractionTaskBuilder() { + task, err := NewRequestedSupplementaryDocTextExtractionTaskBuilder(). + WithManualCheckAlways(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION","config":{"manual_check":"ALWAYS"}} +} + +func TestRequestedSupplementaryDocTextExtractionTaskBuilder_Build_UsesLastManualCheck(t *testing.T) { + task, err := NewRequestedSupplementaryDocTextExtractionTaskBuilder(). + WithManualCheckAlways(). + WithManualCheckNever(). + WithManualCheckFallback(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedSupplementaryDocTextExtractionTaskConfig) + assert.Equal(t, "FALLBACK", config.ManualCheck) +} + +func TestRequestedSupplementaryDocTextExtractionTaskBuilder_WithManualCheckAlways(t *testing.T) { + task, err := NewRequestedSupplementaryDocTextExtractionTaskBuilder(). + WithManualCheckAlways(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedSupplementaryDocTextExtractionTaskConfig) + assert.Equal(t, "ALWAYS", config.ManualCheck) +} + +func TestRequestedSupplementaryDocTextExtractionTaskBuilder_WithManualCheckFallback(t *testing.T) { + task, err := NewRequestedSupplementaryDocTextExtractionTaskBuilder(). + WithManualCheckFallback(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedSupplementaryDocTextExtractionTaskConfig) + assert.Equal(t, "FALLBACK", config.ManualCheck) +} + +func TestRequestedSupplementaryDocTextExtractionTaskBuilder_WithManualCheckNever(t *testing.T) { + task, err := NewRequestedSupplementaryDocTextExtractionTaskBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedSupplementaryDocTextExtractionTaskConfig) + assert.Equal(t, "NEVER", config.ManualCheck) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction.go new file mode 100644 index 0000000..6f45cd1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction.go @@ -0,0 +1,104 @@ +package task + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// RequestedTextExtractionTask requests creation of a Text Extraction Task +type RequestedTextExtractionTask struct { + config RequestedTextExtractionTaskConfig +} + +// Type is the type of the Requested Task +func (t *RequestedTextExtractionTask) Type() string { + return constants.IDDocumentTextDataExtraction +} + +// Config is the configuration of the Requested Task +func (t *RequestedTextExtractionTask) Config() RequestedTaskConfig { + return t.config +} + +// MarshalJSON marshals the RequestedTextExtractionTask to JSON +func (t *RequestedTextExtractionTask) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Config RequestedTaskConfig `json:"config,omitempty"` + }{ + Type: t.Type(), + Config: t.Config(), + }) +} + +// NewRequestedTextExtractionTask creates a new text extraction task +func NewRequestedTextExtractionTask(config RequestedTextExtractionTaskConfig) *RequestedTextExtractionTask { + return &RequestedTextExtractionTask{config} +} + +// RequestedTextExtractionTaskConfig is the configuration applied when creating a Text Extraction Task +type RequestedTextExtractionTaskConfig struct { + ManualCheck string `json:"manual_check,omitempty"` + ChipData string `json:"chip_data,omitempty"` + CreateExpandedDocumentFields bool `json:"create_expanded_document_fields,omitempty"` +} + +// NewRequestedTextExtractionTaskBuilder creates a new RequestedTextExtractionTaskBuilder +func NewRequestedTextExtractionTaskBuilder() *RequestedTextExtractionTaskBuilder { + return &RequestedTextExtractionTaskBuilder{} +} + +// RequestedTextExtractionTaskBuilder builds a RequestedTextExtractionTask +type RequestedTextExtractionTaskBuilder struct { + manualCheck string + chipData string + createExpandedDocumentFields bool +} + +// WithManualCheckAlways sets the value of manual check to "ALWAYS" +func (builder *RequestedTextExtractionTaskBuilder) WithManualCheckAlways() *RequestedTextExtractionTaskBuilder { + builder.manualCheck = constants.Always + return builder +} + +// WithManualCheckFallback sets the value of manual check to "FALLBACK" +func (builder *RequestedTextExtractionTaskBuilder) WithManualCheckFallback() *RequestedTextExtractionTaskBuilder { + builder.manualCheck = constants.Fallback + return builder +} + +// WithManualCheckNever sets the value of manual check to "NEVER" +func (builder *RequestedTextExtractionTaskBuilder) WithManualCheckNever() *RequestedTextExtractionTaskBuilder { + builder.manualCheck = constants.Never + return builder +} + +// WithChipDataDesired sets the value of chip data to "DESIRED" +func (builder *RequestedTextExtractionTaskBuilder) WithChipDataDesired() *RequestedTextExtractionTaskBuilder { + builder.chipData = chipDataDesired + return builder +} + +// WithChipDataIgnore sets the value of chip data to "IGNORE" +func (builder *RequestedTextExtractionTaskBuilder) WithChipDataIgnore() *RequestedTextExtractionTaskBuilder { + builder.chipData = chipDataIgnore + return builder +} + +// withExpandedDocumentFields sets the value of expanded document fields whether its true or false +func (builder *RequestedTextExtractionTaskBuilder) WithExpandedDocumentFields(expandedDocumentFields bool) *RequestedTextExtractionTaskBuilder { + builder.createExpandedDocumentFields = expandedDocumentFields + return builder +} + +// Build builds the RequestedTextExtractionTask +func (builder *RequestedTextExtractionTaskBuilder) Build() (*RequestedTextExtractionTask, error) { + config := RequestedTextExtractionTaskConfig{ + builder.manualCheck, + builder.chipData, + builder.createExpandedDocumentFields, + } + + return NewRequestedTextExtractionTask(config), nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction_test.go new file mode 100644 index 0000000..f05a74c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/create/task/text_extraction_test.go @@ -0,0 +1,115 @@ +package task + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleRequestedTextExtractionTaskBuilder() { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithManualCheckAlways(). + WithChipDataIgnore(). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(task) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"ID_DOCUMENT_TEXT_DATA_EXTRACTION","config":{"manual_check":"ALWAYS","chip_data":"IGNORE"}} +} + +func TestRequestedTextExtractionTaskBuilder_Build_UsesLastManualCheck(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithManualCheckAlways(). + WithManualCheckNever(). + WithManualCheckFallback(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, "FALLBACK", config.ManualCheck) +} + +func TestRequestedTextExtractionTaskBuilder_WithManualCheckAlways(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithManualCheckAlways(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, "ALWAYS", config.ManualCheck) +} + +func TestRequestedTextExtractionTaskBuilder_WithManualCheckFallback(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithManualCheckFallback(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, "FALLBACK", config.ManualCheck) +} + +func TestRequestedTextExtractionTaskBuilder_WithManualCheckNever(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithManualCheckNever(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, "NEVER", config.ManualCheck) +} + +func TestRequestedTextExtractionTaskBuilder_WithChipDataDesired(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithChipDataDesired(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, "DESIRED", config.ChipData) +} + +func TestRequestedTextExtractionTaskBuilder_WithChipDataIgnore(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithChipDataIgnore(). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, "IGNORE", config.ChipData) +} + +func TestRequestedTextExtractionTaskBuilder_WithExpandedDocumentFields(t *testing.T) { + task, err := NewRequestedTextExtractionTaskBuilder(). + WithExpandedDocumentFields(true). + Build() + if err != nil { + t.Fail() + } + + config := task.Config().(RequestedTextExtractionTaskConfig) + assert.Equal(t, true, config.CreateExpandedDocumentFields) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_preview.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_preview.go new file mode 100644 index 0000000..4e6e36f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_preview.go @@ -0,0 +1,7 @@ +package retrieve + +// AdvancedIdentityProfilePreview contains info about the media needed to +// retrieve the Advanced Identity Profile Preview. +type AdvancedIdentityProfilePreview struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_response.go new file mode 100644 index 0000000..fe2e5de --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/advanced_identity_profile_response.go @@ -0,0 +1,10 @@ +package retrieve + +// AdvancedIdentityProfileResponse contains the SubjectId, the Result/FailureReasonResponse, verified identity details, +// and the verification reports that certifies how the identity was verified and how the verification levels were achieved. +type AdvancedIdentityProfileResponse struct { + SubjectId string `json:"subject_id"` + Result string `json:"result"` + FailureReasonResponse FailureReasonResponse `json:"failure_reason"` + Report map[string]interface{} `json:"identity_profile_report"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/breakdown_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/breakdown_response.go new file mode 100644 index 0000000..a609f6d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/breakdown_response.go @@ -0,0 +1,8 @@ +package retrieve + +// BreakdownResponse represents one breakdown item for a given check +type BreakdownResponse struct { + SubCheck string `json:"sub_check"` + Result string `json:"result"` + Details []*DetailsResponse `json:"details"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/ca_sources.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/ca_sources.go new file mode 100644 index 0000000..72bfe6b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/ca_sources.go @@ -0,0 +1,7 @@ +package retrieve + +type CASourcesResponse struct { + Type string `json:"type"` + SearchProfile string `json:"search_profile"` + Types []string `json:"types"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response.go new file mode 100644 index 0000000..5b57703 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response.go @@ -0,0 +1,144 @@ +package retrieve + +import ( + "encoding/json" + "fmt" +) + +type CaptureResponse struct { + BiometricConsent string `json:"biometric_consent"` + RequiredResources []RequiredResourceResponse `json:"-"` +} + +type captureResponseAlias struct { + BiometricConsent string `json:"biometric_consent"` + RequiredResources []json.RawMessage `json:"required_resources"` +} + +func (c *CaptureResponse) UnmarshalJSON(data []byte) error { + aux := captureResponseAlias{} + + if err := json.Unmarshal(data, &aux); err != nil { + return fmt.Errorf("failed to unmarshal CaptureResponse: %w", err) + } + + c.BiometricConsent = aux.BiometricConsent + c.RequiredResources = make([]RequiredResourceResponse, 0, len(aux.RequiredResources)) + + for _, raw := range aux.RequiredResources { + resource, err := unmarshalResource(raw) + if err != nil { + return err + } + c.RequiredResources = append(c.RequiredResources, resource) + } + + return nil +} + +func unmarshalResource(raw json.RawMessage) (RequiredResourceResponse, error) { + var base BaseRequiredResource + if err := json.Unmarshal(raw, &base); err != nil { + return nil, fmt.Errorf("failed to unmarshal base resource: %w", err) + } + + switch base.Type { + case "ID_DOCUMENT": + var r RequiredIdDocumentResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal ID_DOCUMENT resource: %w", err) + } + return &r, nil + + case "SUPPLEMENTARY_DOCUMENT": + var r RequiredSupplementaryDocumentResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal SUPPLEMENTARY_DOCUMENT resource: %w", err) + } + return &r, nil + + case "LIVENESS": + switch base.LivenessType { + case "ZOOM": + var r RequiredZoomLivenessResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal ZOOM liveness resource: %w", err) + } + return &r, nil + + case "STATIC": + var r RequiredStaticLivenessResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal STATIC liveness resource: %w", err) + } + return &r, nil + + default: + var r RequiredLivenessResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal generic LIVENESS resource: %w", err) + } + return &r, nil + } + + case "FACE_CAPTURE": + var r RequiredFaceCaptureResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal FACE_CAPTURE resource: %w", err) + } + return &r, nil + + default: + var r UnknownRequiredResourceResponse + if err := json.Unmarshal(raw, &r); err != nil { + return nil, fmt.Errorf("failed to unmarshal unknown resource type: %w", err) + } + return &r, nil + } +} + +// Generic filter helper +func filterByType[T RequiredResourceResponse](resources []RequiredResourceResponse) []T { + var filtered []T + for _, r := range resources { + if typed, ok := r.(T); ok { + filtered = append(filtered, typed) + } + } + return filtered +} + +func (c *CaptureResponse) GetDocumentResourceRequirements() []RequiredResourceResponse { + var docs []RequiredResourceResponse + for _, r := range c.RequiredResources { + switch r.(type) { + case *RequiredIdDocumentResourceResponse, *RequiredSupplementaryDocumentResourceResponse: + docs = append(docs, r) + } + } + return docs +} + +func (c *CaptureResponse) GetIdDocumentResourceRequirements() []*RequiredIdDocumentResourceResponse { + return filterByType[*RequiredIdDocumentResourceResponse](c.RequiredResources) +} + +func (c *CaptureResponse) GetSupplementaryResourceRequirements() []*RequiredSupplementaryDocumentResourceResponse { + return filterByType[*RequiredSupplementaryDocumentResourceResponse](c.RequiredResources) +} + +func (c *CaptureResponse) GetZoomLivenessResourceRequirements() []*RequiredZoomLivenessResourceResponse { + return filterByType[*RequiredZoomLivenessResourceResponse](c.RequiredResources) +} + +func (c *CaptureResponse) GetStaticLivenessResourceRequirements() []*RequiredStaticLivenessResourceResponse { + return filterByType[*RequiredStaticLivenessResourceResponse](c.RequiredResources) +} + +func (c *CaptureResponse) GetLivenessResourceRequirements() []*RequiredLivenessResourceResponse { + return filterByType[*RequiredLivenessResourceResponse](c.RequiredResources) +} + +func (c *CaptureResponse) GetFaceCaptureResourceRequirements() []*RequiredFaceCaptureResourceResponse { + return filterByType[*RequiredFaceCaptureResourceResponse](c.RequiredResources) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response_test.go new file mode 100644 index 0000000..9e75fc8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/capture_response_test.go @@ -0,0 +1,75 @@ +package retrieve + +import ( + "encoding/json" + "testing" + + "gotest.tools/v3/assert" +) + +func TestCaptureResponse_UnmarshalJSON(t *testing.T) { + jsonData := []byte(`{ + "biometric_consent": "given", + "required_resources": [ + {"type": "ID_DOCUMENT", "id": "id1", "state": "pending"}, + {"type": "SUPPLEMENTARY_DOCUMENT", "id": "id2", "state": "pending"}, + {"type": "LIVENESS", "id": "id3", "state": "pending", "liveness_type": "ZOOM"}, + {"type": "LIVENESS", "id": "id4", "state": "pending", "liveness_type": "STATIC"}, + {"type": "FACE_CAPTURE", "id": "id5", "state": "pending"}, + {"type": "UNKNOWN_TYPE", "id": "id6", "state": "pending"} + ] + }`) + + var c CaptureResponse + err := json.Unmarshal(jsonData, &c) + assert.NilError(t, err) + assert.Equal(t, "given", c.BiometricConsent) + assert.Equal(t, 6, len(c.RequiredResources)) + + _, ok := c.RequiredResources[0].(*RequiredIdDocumentResourceResponse) + assert.Assert(t, ok) + + _, ok = c.RequiredResources[1].(*RequiredSupplementaryDocumentResourceResponse) + assert.Assert(t, ok) + + _, ok = c.RequiredResources[2].(*RequiredZoomLivenessResourceResponse) + assert.Assert(t, ok) + + _, ok = c.RequiredResources[3].(*RequiredStaticLivenessResourceResponse) + assert.Assert(t, ok) + + _, ok = c.RequiredResources[4].(*RequiredFaceCaptureResourceResponse) + assert.Assert(t, ok) + + _, ok = c.RequiredResources[5].(*UnknownRequiredResourceResponse) + assert.Assert(t, ok) +} + +func TestCaptureResponse_Getters(t *testing.T) { + c := CaptureResponse{ + RequiredResources: []RequiredResourceResponse{ + &RequiredIdDocumentResourceResponse{BaseRequiredResource{Type: "ID_DOCUMENT", ID: "id1"}}, + &RequiredSupplementaryDocumentResourceResponse{BaseRequiredResource{Type: "SUPPLEMENTARY_DOCUMENT", ID: "id2"}}, + &RequiredZoomLivenessResourceResponse{BaseRequiredResource{Type: "LIVENESS", ID: "id3", LivenessType: "ZOOM"}}, + &RequiredStaticLivenessResourceResponse{BaseRequiredResource{Type: "LIVENESS", ID: "id4", LivenessType: "STATIC"}}, + &RequiredFaceCaptureResourceResponse{BaseRequiredResource{Type: "FACE_CAPTURE", ID: "id5"}}, + }, + } + + assert.Equal(t, 2, len(c.GetDocumentResourceRequirements())) + assert.Equal(t, 1, len(c.GetIdDocumentResourceRequirements())) + assert.Equal(t, 1, len(c.GetSupplementaryResourceRequirements())) + assert.Equal(t, 1, len(c.GetZoomLivenessResourceRequirements())) + assert.Equal(t, 1, len(c.GetStaticLivenessResourceRequirements())) + assert.Equal(t, 1, len(c.GetFaceCaptureResourceRequirements())) +} + +func TestCaptureResponse_EmptyResources(t *testing.T) { + jsonData := []byte(`{"biometric_consent": "none", "required_resources": []}`) + + var c CaptureResponse + err := json.Unmarshal(jsonData, &c) + assert.NilError(t, err) + assert.Equal(t, "none", c.BiometricConsent) + assert.Equal(t, 0, len(c.RequiredResources)) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/check_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/check_response.go new file mode 100644 index 0000000..0643b75 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/check_response.go @@ -0,0 +1,68 @@ +package retrieve + +import ( + "time" +) + +// CheckResponse represents the attributes of a check, for any given session +type CheckResponse struct { + ID string `json:"id"` + Type string `json:"type"` + State string `json:"state"` + ResourcesUsed []string `json:"resources_used"` + GeneratedMedia []*GeneratedMedia `json:"generated_media"` + GeneratedProfile *GeneratedProfileResponse `json:"generated_profile"` + Report *ReportResponse `json:"report"` + Created *time.Time `json:"created"` + LastUpdated *time.Time `json:"last_updated"` +} + +// AuthenticityCheckResponse represents a Document Authenticity check for a given session +type AuthenticityCheckResponse struct { + *CheckResponse +} + +// FaceMatchCheckResponse represents a FaceMatch Check for a given session +type FaceMatchCheckResponse struct { + *CheckResponse +} + +// LivenessCheckResponse represents a Liveness Check for a given session +type LivenessCheckResponse struct { + *CheckResponse +} + +// TextDataCheckResponse represents a Text Data check for a given session +type TextDataCheckResponse struct { + *CheckResponse +} + +// IDDocumentComparisonCheckResponse represents an identity document comparison check for a given session +type IDDocumentComparisonCheckResponse struct { + *CheckResponse +} + +// SupplementaryDocumentTextDataCheckResponse represents a supplementary document text data check for a given session +type SupplementaryDocumentTextDataCheckResponse struct { + *CheckResponse +} + +// ThirdPartyIdentityCheckResponse represents a check with an external credit reference agency +type ThirdPartyIdentityCheckResponse struct { + *CheckResponse +} + +// WatchlistScreeningCheckResponse represents a watchlist screening check +type WatchlistScreeningCheckResponse struct { + *CheckResponse +} + +// WatchlistAdvancedCACheckResponse represents an advanced CA watchlist screening check +type WatchlistAdvancedCACheckResponse struct { + *CheckResponse +} + +// FaceComparisonCheckResponse represents an advanced CA watchlist screening check +type FaceComparisonCheckResponse struct { + *CheckResponse +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/details_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/details_response.go new file mode 100644 index 0000000..386d8c6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/details_response.go @@ -0,0 +1,7 @@ +package retrieve + +// DetailsResponse represents a specific detail for a breakdown +type DetailsResponse struct { + Name string `json:"name"` + Value string `json:"value"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_fields_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_fields_response.go new file mode 100644 index 0000000..328a0d2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_fields_response.go @@ -0,0 +1,6 @@ +package retrieve + +// DocumentFieldsResponse represents the document fields in a document +type DocumentFieldsResponse struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_id_photo_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_id_photo_response.go new file mode 100644 index 0000000..5a52cc1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/document_id_photo_response.go @@ -0,0 +1,6 @@ +package retrieve + +// DocumentIDPhotoResponse represents the photo from a document +type DocumentIDPhotoResponse struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/expanded_document_fields_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/expanded_document_fields_response.go new file mode 100644 index 0000000..a55768e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/expanded_document_fields_response.go @@ -0,0 +1,6 @@ +package retrieve + +// DocumentFieldsResponse represents the document fields in a document +type ExpandedDocumentFieldsResponse struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response.go new file mode 100644 index 0000000..3c04311 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response.go @@ -0,0 +1,18 @@ +// face_capture_resource_response.go +package retrieve + +// FaceCaptureResourceResponse models the response for face capture resource. +type FaceCaptureResourceResponse struct { + ID string `json:"id"` + Frames int `json:"frames"` +} + +// GetID returns the ID of the face capture resource. +func (r *FaceCaptureResourceResponse) GetID() string { + return r.ID +} + +// GetFrames returns the number of image frames required. +func (r *FaceCaptureResourceResponse) GetFrames() int { + return r.Frames +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response_test.go new file mode 100644 index 0000000..e1bcf7b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_capture_resource_response_test.go @@ -0,0 +1,32 @@ +// face_capture_resource_response_test.go +package retrieve + +import ( + "encoding/json" + "gotest.tools/v3/assert" + "testing" +) + +func TestFaceCaptureResourceResponse_Getters(t *testing.T) { + resp := &FaceCaptureResourceResponse{ + ID: "face-resource-id", + Frames: 3, + } + + assert.Equal(t, resp.GetID(), "face-resource-id") + assert.Equal(t, resp.GetFrames(), 3) +} + +func TestFaceCaptureResourceResponse_UnmarshalJSON(t *testing.T) { + jsonData := `{ + "id": "resource-123", + "frames": 5 + }` + + var resp FaceCaptureResourceResponse + err := json.Unmarshal([]byte(jsonData), &resp) + assert.NilError(t, err) + + assert.Equal(t, resp.ID, "resource-123") + assert.Equal(t, resp.Frames, 5) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_map_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_map_response.go new file mode 100644 index 0000000..f3a6d64 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/face_map_response.go @@ -0,0 +1,6 @@ +package retrieve + +// FaceMapResponse represents a FaceMap response object +type FaceMapResponse struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/failure_reason_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/failure_reason_response.go new file mode 100644 index 0000000..eaf10e3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/failure_reason_response.go @@ -0,0 +1,14 @@ +package retrieve + +type FailureReasonResponse struct { + ReasonCode string `json:"reason_code"` + RequirementsNotMetDetails []RequirementsNotMetDetail `json:"requirements_not_met_details"` +} + +type RequirementsNotMetDetail struct { + FailureType string `json:"failure_type"` + DocumentType string `json:"document_type"` + DocumentCountryIsoCode string `json:"document_country_iso_code"` + AuditId string `json:"audit_id"` + Details string `json:"details"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/file_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/file_response.go new file mode 100644 index 0000000..5893045 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/file_response.go @@ -0,0 +1,6 @@ +package retrieve + +// FileResponse represents a file +type FileResponse struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/frame_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/frame_response.go new file mode 100644 index 0000000..9055053 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/frame_response.go @@ -0,0 +1,6 @@ +package retrieve + +// FrameResponse represents a frame of a resource +type FrameResponse struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_check_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_check_response.go new file mode 100644 index 0000000..b244efa --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_check_response.go @@ -0,0 +1,17 @@ +package retrieve + +// GeneratedCheckResponse represents a check response that has been generated by the session +type GeneratedCheckResponse struct { + ID string `json:"id"` + Type string `json:"type"` +} + +// GeneratedTextDataCheckResponse represents a text data check response +type GeneratedTextDataCheckResponse struct { + *GeneratedCheckResponse +} + +// GeneratedSupplementaryTextDataCheckResponse represents a supplementary text data check response +type GeneratedSupplementaryTextDataCheckResponse struct { + *GeneratedCheckResponse +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_media.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_media.go new file mode 100644 index 0000000..540b51e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_media.go @@ -0,0 +1,7 @@ +package retrieve + +// GeneratedMedia represents media that has been generated by the session +type GeneratedMedia struct { + ID string `json:"id"` + Type string `json:"type"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_profile.go new file mode 100644 index 0000000..99ba465 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/generated_profile.go @@ -0,0 +1,6 @@ +package retrieve + +// GeneratedProfileResponse represents a profile that has been generated by the session +type GeneratedProfileResponse struct { + Media MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result.go new file mode 100644 index 0000000..1597d5f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result.go @@ -0,0 +1,154 @@ +package retrieve + +import ( + "encoding/json" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// GetSessionResult contains the information about a created session +type GetSessionResult struct { + ClientSessionTokenTTL int `json:"client_session_token_ttl"` + ClientSessionToken string `json:"client_session_token"` + SessionID string `json:"session_id"` + UserTrackingID string `json:"user_tracking_id"` + State string `json:"state"` + Checks []*CheckResponse `json:"checks"` + Resources *ResourceContainer `json:"resources"` + BiometricConsentTimestamp *time.Time `json:"biometric_consent"` + IdentityProfileResponse *IdentityProfileResponse `json:"identity_profile"` + AdvancedIdentityProfileResponse *AdvancedIdentityProfileResponse `json:"advanced_identity_profile"` + IdentityProfilePreview *IdentityProfilePreview `json:"identity_profile_preview"` + AdvancedIdentityProfilePreview *AdvancedIdentityProfilePreview `json:"advanced_identity_profile_preview"` + ImportTokenResponse *ImportTokenResponse `json:"import_token"` + authenticityChecks []*AuthenticityCheckResponse + faceMatchChecks []*FaceMatchCheckResponse + textDataChecks []*TextDataCheckResponse + livenessChecks []*LivenessCheckResponse + thirdPartyIdentityChecks []*ThirdPartyIdentityCheckResponse + idDocumentComparisonChecks []*IDDocumentComparisonCheckResponse + supplementaryDocumentTextDataChecks []*SupplementaryDocumentTextDataCheckResponse + watchlistScreeningChecks []*WatchlistScreeningCheckResponse + watchlistAdvancedCAChecks []*WatchlistAdvancedCACheckResponse + faceComparisonChecks []*FaceComparisonCheckResponse +} + +// AuthenticityChecks filters the checks, returning only document authenticity checks +func (g *GetSessionResult) AuthenticityChecks() []*AuthenticityCheckResponse { + return g.authenticityChecks +} + +// FaceMatchChecks filters the checks, returning only FaceMatch checks +func (g *GetSessionResult) FaceMatchChecks() []*FaceMatchCheckResponse { + return g.faceMatchChecks +} + +// TextDataChecks filters the checks, returning only ID Document Text Data checks +// Deprecated: replaced by IDDocumentTextDataChecks() +func (g *GetSessionResult) TextDataChecks() []*TextDataCheckResponse { + return g.IDDocumentTextDataChecks() +} + +// ThirdPartyIdentityChecks filters the checks, returning only external credit reference agency checks +func (g *GetSessionResult) ThirdPartyIdentityChecks() []*ThirdPartyIdentityCheckResponse { + return g.thirdPartyIdentityChecks +} + +// IDDocumentTextDataChecks filters the checks, returning only ID Document Text Data checks +func (g *GetSessionResult) IDDocumentTextDataChecks() []*TextDataCheckResponse { + return g.textDataChecks +} + +// LivenessChecks filters the checks, returning only Liveness checks +func (g *GetSessionResult) LivenessChecks() []*LivenessCheckResponse { + return g.livenessChecks +} + +// IDDocumentComparisonChecks filters the checks, returning only the identity document comparison checks +func (g *GetSessionResult) IDDocumentComparisonChecks() []*IDDocumentComparisonCheckResponse { + return g.idDocumentComparisonChecks +} + +// SupplementaryDocumentTextDataChecks filters the checks, returning only the supplementary document text data checks +func (g *GetSessionResult) SupplementaryDocumentTextDataChecks() []*SupplementaryDocumentTextDataCheckResponse { + return g.supplementaryDocumentTextDataChecks +} + +// WatchlistScreeningChecks filters the checks, returning only the Watchlist Screening checks +func (g *GetSessionResult) WatchlistScreeningChecks() []*WatchlistScreeningCheckResponse { + return g.watchlistScreeningChecks +} + +// WatchlistAdvancedCAChecks filters the checks, returning only the Watchlist Advanced CA Screening checks +func (g *GetSessionResult) WatchlistAdvancedCAChecks() []*WatchlistAdvancedCACheckResponse { + return g.watchlistAdvancedCAChecks +} + +// FaceComparisonChecks filters the checks, returning only FaceComparison checks +func (g *GetSessionResult) FaceComparisonChecks() []*FaceComparisonCheckResponse { + return g.faceComparisonChecks +} + +// UnmarshalJSON handles the custom JSON unmarshalling +func (g *GetSessionResult) UnmarshalJSON(data []byte) error { + type result GetSessionResult // declared as "type" to prevent recursive unmarshalling + if err := json.Unmarshal(data, (*result)(g)); err != nil { + return err + } + + for _, check := range g.Checks { + switch check.Type { + case constants.IDDocumentAuthenticity: + g.authenticityChecks = append(g.authenticityChecks, &AuthenticityCheckResponse{CheckResponse: check}) + + case constants.IDDocumentFaceMatch: + g.faceMatchChecks = append(g.faceMatchChecks, &FaceMatchCheckResponse{CheckResponse: check}) + + case constants.IDDocumentTextDataCheck: + g.textDataChecks = append(g.textDataChecks, &TextDataCheckResponse{CheckResponse: check}) + + case constants.Liveness: + g.livenessChecks = append(g.livenessChecks, &LivenessCheckResponse{CheckResponse: check}) + + case constants.IDDocumentComparison: + g.idDocumentComparisonChecks = append(g.idDocumentComparisonChecks, &IDDocumentComparisonCheckResponse{CheckResponse: check}) + + case constants.FaceComparison: + g.faceComparisonChecks = append(g.faceComparisonChecks, &FaceComparisonCheckResponse{CheckResponse: check}) + + case constants.ThirdPartyIdentityCheck: + g.thirdPartyIdentityChecks = append( + g.thirdPartyIdentityChecks, + &ThirdPartyIdentityCheckResponse{ + CheckResponse: check, + }) + + case constants.SupplementaryDocumentTextDataCheck: + g.supplementaryDocumentTextDataChecks = append( + g.supplementaryDocumentTextDataChecks, + &SupplementaryDocumentTextDataCheckResponse{ + CheckResponse: check, + }, + ) + + case constants.WatchlistScreening: + g.watchlistScreeningChecks = append( + g.watchlistScreeningChecks, + &WatchlistScreeningCheckResponse{ + CheckResponse: check, + }, + ) + + case constants.WatchlistAdvancedCA: + g.watchlistAdvancedCAChecks = append( + g.watchlistAdvancedCAChecks, + &WatchlistAdvancedCACheckResponse{ + CheckResponse: check, + }, + ) + } + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result_test.go new file mode 100644 index 0000000..14457de --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/get_session_result_test.go @@ -0,0 +1,310 @@ +package retrieve_test + +import ( + "encoding/json" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + "github.com/getyoti/yoti-go-sdk/v3/docscan/session/retrieve" + "github.com/getyoti/yoti-go-sdk/v3/file" + "gotest.tools/v3/assert" +) + +func TestGetSessionResult_UnmarshalJSON(t *testing.T) { + authenticityCheckResponse := &retrieve.CheckResponse{ + Type: constants.IDDocumentAuthenticity, + State: "DONE", + } + + testDate := time.Date(2020, 01, 01, 1, 2, 3, 4, time.UTC) + faceMatchCheckResponse := &retrieve.CheckResponse{ + Type: constants.IDDocumentFaceMatch, + Created: &testDate, + } + + textDataCheckResponse := &retrieve.CheckResponse{ + Type: constants.IDDocumentTextDataCheck, + Report: &retrieve.ReportResponse{}, + } + + livenessCheckResponse := &retrieve.CheckResponse{ + Type: constants.Liveness, + LastUpdated: &testDate, + } + + idDocComparisonCheckResponse := &retrieve.CheckResponse{ + Type: constants.IDDocumentComparison, + State: "PENDING", + } + + faceComparisonCheckResponse := &retrieve.CheckResponse{ + Type: constants.FaceComparison, + State: "PENDING", + } + + thirdPartyIdentityCheckResponse := &retrieve.CheckResponse{ + Type: constants.ThirdPartyIdentityCheck, + State: "PENDING", + } + + supplementaryTextDataCheckResponse := &retrieve.CheckResponse{ + Type: constants.SupplementaryDocumentTextDataCheck, + Report: &retrieve.ReportResponse{}, + } + + watchlistScreeningCheckResponse := &retrieve.CheckResponse{ + Type: constants.WatchlistScreening, + State: "DONE", + } + + advancedWatchlistScreeningCheckResponse := &retrieve.CheckResponse{ + Type: constants.WatchlistAdvancedCA, + State: "PENDING", + } + + var checks []*retrieve.CheckResponse + checks = append(checks, &retrieve.CheckResponse{Type: "OTHER_TYPE", ID: "id"}) + checks = append(checks, authenticityCheckResponse) + checks = append(checks, faceMatchCheckResponse) + checks = append(checks, textDataCheckResponse) + checks = append(checks, livenessCheckResponse) + checks = append(checks, idDocComparisonCheckResponse) + checks = append(checks, faceComparisonCheckResponse) + checks = append(checks, thirdPartyIdentityCheckResponse) + checks = append(checks, supplementaryTextDataCheckResponse) + checks = append(checks, watchlistScreeningCheckResponse) + checks = append(checks, advancedWatchlistScreeningCheckResponse) + + biometricConsentTimestamp := time.Date(2020, 01, 01, 1, 2, 3, 4, time.UTC) + + getSessionResult := retrieve.GetSessionResult{ + Checks: checks, + BiometricConsentTimestamp: &biometricConsentTimestamp, + } + marshalled, err := json.Marshal(&getSessionResult) + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = json.Unmarshal(marshalled, &result) + assert.NilError(t, err) + + assert.Equal(t, 11, len(result.Checks)) + + assert.Equal(t, 1, len(result.AuthenticityChecks())) + assert.Equal(t, "DONE", result.AuthenticityChecks()[0].State) + + assert.Equal(t, 1, len(result.FaceMatchChecks())) + assert.Check(t, result.FaceMatchChecks()[0].Created.Equal(testDate)) + + assert.Equal(t, 1, len(result.TextDataChecks())) + assert.DeepEqual(t, &retrieve.ReportResponse{}, result.TextDataChecks()[0].Report) + + assert.Equal(t, 1, len(result.IDDocumentTextDataChecks())) + assert.DeepEqual(t, &retrieve.ReportResponse{}, result.IDDocumentTextDataChecks()[0].Report) + + assert.Equal(t, 1, len(result.LivenessChecks())) + assert.Check(t, result.LivenessChecks()[0].LastUpdated.Equal(testDate)) + + assert.Equal(t, 1, len(result.IDDocumentComparisonChecks())) + assert.Equal(t, "PENDING", result.IDDocumentComparisonChecks()[0].State) + + assert.Equal(t, 1, len(result.FaceComparisonChecks())) + assert.Equal(t, "PENDING", result.FaceComparisonChecks()[0].State) + + assert.Equal(t, 1, len(result.ThirdPartyIdentityChecks())) + assert.Equal(t, "PENDING", result.ThirdPartyIdentityChecks()[0].State) + + assert.Equal(t, 1, len(result.SupplementaryDocumentTextDataChecks())) + assert.DeepEqual(t, &retrieve.ReportResponse{}, result.SupplementaryDocumentTextDataChecks()[0].Report) + assert.Assert(t, result.SupplementaryDocumentTextDataChecks()[0].Report.WatchlistSummary == nil) + assert.Assert(t, result.SupplementaryDocumentTextDataChecks()[0].GeneratedProfile == nil) + + assert.Equal(t, 1, len(result.WatchlistScreeningChecks())) + assert.DeepEqual(t, "DONE", result.WatchlistScreeningChecks()[0].State) + + assert.Equal(t, 1, len(result.WatchlistAdvancedCAChecks())) + assert.DeepEqual(t, "PENDING", result.WatchlistAdvancedCAChecks()[0].State) + + assert.Equal(t, biometricConsentTimestamp, *result.BiometricConsentTimestamp) +} + +func TestGetSessionResult_UnmarshalJSON_Watchlist(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/watchlist_screening.json") + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = result.UnmarshalJSON(bytes) + assert.NilError(t, err) + + assert.Equal(t, 1, len(result.WatchlistScreeningChecks())) + watchlistScreeningCheck := result.WatchlistScreeningChecks()[0] + assert.Equal(t, watchlistScreeningCheck.GeneratedProfile.Media.Type, "JSON") + + watchlistSummary := watchlistScreeningCheck.Report.WatchlistSummary + + assert.Equal(t, 0, watchlistSummary.TotalHits) + assert.Equal(t, 2, len(watchlistSummary.SearchConfig.Categories)) + assert.Equal(t, watchlistSummary.SearchConfig.Categories[0], "ADVERSE-MEDIA") + assert.Equal(t, watchlistSummary.SearchConfig.Categories[1], "SANCTIONS") + assert.Equal(t, watchlistSummary.RawResults.Media.Type, "JSON") + assert.Equal(t, watchlistSummary.AssociatedCountryCodes[0], "GBR") +} + +func TestGetSessionResult_UnmarshalJSON_Watchlist_Advanced_CA(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/watchlist_advanced_ca_profile_custom.json") + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = result.UnmarshalJSON(bytes) + assert.NilError(t, err) + + assert.Equal(t, 1, len(result.WatchlistAdvancedCAChecks())) + watchlistAdvancedCACheck := result.WatchlistAdvancedCAChecks()[0] + assert.Equal(t, 1, len(watchlistAdvancedCACheck.GeneratedMedia)) + assert.Equal(t, watchlistAdvancedCACheck.GeneratedMedia[0].Type, "JSON") + + assert.Equal(t, watchlistAdvancedCACheck.GeneratedProfile.Media.Type, "JSON") + + watchlistSummary := watchlistAdvancedCACheck.Report.WatchlistSummary + assert.Equal(t, watchlistSummary.RawResults.Media.Type, "JSON") + assert.Equal(t, watchlistSummary.AssociatedCountryCodes[0], "GBR") + assert.Equal(t, watchlistSummary.RawResults.Media.Type, "JSON") + assert.Equal(t, watchlistSummary.AssociatedCountryCodes[0], "GBR") + + searchConfig := watchlistSummary.SearchConfig + assert.Equal(t, "WITH_CUSTOM_ACCOUNT", searchConfig.Type) + assert.Equal(t, true, searchConfig.RemoveDeceased) + assert.Equal(t, true, searchConfig.ShareURL) + assert.Equal(t, "FUZZY", searchConfig.MatchingStrategy.Type) + assert.Equal(t, 0.6, searchConfig.MatchingStrategy.Fuzziness) + assert.Equal(t, "PROFILE", searchConfig.Sources.Type) + assert.Equal(t, "b41d82de-9a1d-4494-97a6-8b1b9895a908", searchConfig.Sources.SearchProfile) + assert.Equal(t, "gQ2vf0STnF5nGy9SSdyuGJuYMFfNASmV", searchConfig.APIKey) + assert.Equal(t, "111111", searchConfig.ClientRef) + assert.Equal(t, true, searchConfig.Monitoring) +} + +func TestGetSessionResult_UnmarshalJSON_Invalid(t *testing.T) { + var result retrieve.GetSessionResult + err := result.UnmarshalJSON([]byte("some-invalid-json")) + assert.ErrorContains(t, err, "invalid character") +} + +func TestGetSessionResult_UnmarshalJSON_WithoutBiometricConsentTimestamp(t *testing.T) { + var result retrieve.GetSessionResult + err := result.UnmarshalJSON([]byte("{}")) + assert.NilError(t, err) + assert.Check(t, result.BiometricConsentTimestamp == nil) +} + +func TestGetSessionResult_UnmarshalJSON_IdentityProfile(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/GetSessionResultWithIdentityProfile.json") + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = result.UnmarshalJSON(bytes) + assert.NilError(t, err) + + identityProfile := result.IdentityProfileResponse + assert.Assert(t, identityProfile != nil) + + assert.Equal(t, identityProfile.SubjectId, "someStringHere") + assert.Equal(t, identityProfile.Result, "DONE") + assert.DeepEqual(t, identityProfile.FailureReasonResponse, retrieve.FailureReasonResponse{ + ReasonCode: "MANDATORY_DOCUMENT_COULD_NOT_BE_PROVIDED", + RequirementsNotMetDetails: []retrieve.RequirementsNotMetDetail{ + { + FailureType: "ID_DOCUMENT_AUTHENTICITY", + DocumentType: "PASSPORT", + DocumentCountryIsoCode: "GBR", + AuditId: "a526df5f-a9c1-4e57-8aa3-919256d8e280", + Details: "INCORRECT_DOCUMENT_TYPE", + }, + }, + }) + + assert.NilError(t, err) + tf, ok := identityProfile.Report["trust_framework"].(string) + assert.Equal(t, ok, true) + assert.Equal(t, tf, "UK_TFIDA") + media, ok := identityProfile.Report["media"].(map[string]interface{}) + assert.Equal(t, ok, true) + mid, ok := media["id"].(string) + assert.Equal(t, ok, true) + assert.Equal(t, mid, "c69ff2db-6caf-4e74-8386-037711bbc8d7") +} + +func TestGetSessionResult_UnmarshalJSON_AdvancedIdentityProfile(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/GetSessionResultWithAdvancedIdentityProfile.json") + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = result.UnmarshalJSON(bytes) + assert.NilError(t, err) + + identityProfile := result.AdvancedIdentityProfileResponse + assert.Assert(t, identityProfile != nil) + + assert.Equal(t, identityProfile.SubjectId, "someStringHere") + assert.Equal(t, identityProfile.Result, "DONE") + assert.DeepEqual(t, identityProfile.FailureReasonResponse, + retrieve.FailureReasonResponse{ + ReasonCode: "MANDATORY_DOCUMENT_COULD_NOT_BE_PROVIDED", + RequirementsNotMetDetails: []retrieve.RequirementsNotMetDetail{ + { + FailureType: "ID_DOCUMENT_AUTHENTICITY", + DocumentType: "PASSPORT", + DocumentCountryIsoCode: "GBR", + AuditId: "a526df5f-a9c1-4e57-8aa3-919256d8e280", + Details: "INCORRECT_DOCUMENT_TYPE", + }}, + }, + ) + + compliances, ok := identityProfile.Report["compliance"].([]interface{}) + assert.Equal(t, ok, true) + assert.Equal(t, len(compliances), 1) + + compliance, ok := compliances[0].(map[string]interface{}) + assert.Equal(t, ok, true) + assert.Equal(t, compliance["trust_framework"], "UK_TFIDA") + + media, ok := identityProfile.Report["media"].(map[string]interface{}) + assert.Equal(t, ok, true) + mid, ok := media["id"].(string) + assert.Equal(t, ok, true) + assert.Equal(t, mid, "c69ff2db-6caf-4e74-8386-037711bbc8d7") +} + +func TestGetSessionResult_UnmarshalJSON_IdentityProfilePreview(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/GetSessionResultWithIdentityProfile.json") + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = result.UnmarshalJSON(bytes) + assert.NilError(t, err) + + identityProfilePreview := result.IdentityProfilePreview + assert.Assert(t, identityProfilePreview != nil) + + assert.Assert(t, identityProfilePreview.Media != nil) + assert.Equal(t, identityProfilePreview.Media.ID, "3fa85f64-5717-4562-b3fc-2c963f66afa6") + assert.Equal(t, identityProfilePreview.Media.Type, "IMAGE") +} + +func TestGetSessionResult_UnmarshalJSON_AdvancedIdentityProfilePreview(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/GetSessionResultWithAdvancedIdentityProfile.json") + assert.NilError(t, err) + + var result retrieve.GetSessionResult + err = result.UnmarshalJSON(bytes) + assert.NilError(t, err) + + identityProfilePreview := result.AdvancedIdentityProfilePreview + assert.Assert(t, identityProfilePreview != nil) + + assert.Assert(t, identityProfilePreview.Media != nil) + assert.Equal(t, identityProfilePreview.Media.ID, "3fa85f64-5717-4562-b3fc-2c963f66afa6") + assert.Equal(t, identityProfilePreview.Media.Type, "IMAGE") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response.go new file mode 100644 index 0000000..b48bfe0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response.go @@ -0,0 +1,45 @@ +package retrieve + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// IDDocumentResourceResponse represents an Identity Document resource for a given session +type IDDocumentResourceResponse struct { + *ResourceResponse + // DocumentType is the identity document type, e.g. "PASSPORT" + DocumentType string `json:"document_type"` + // IssuingCountry is the issuing country of the identity document + IssuingCountry string `json:"issuing_country"` + // Pages are the individual pages of the identity document + Pages []*PageResponse `json:"pages"` + // DocumentFields are the associated document fields of a document + DocumentFields *DocumentFieldsResponse `json:"document_fields"` + ExpandedDocumentFields *ExpandedDocumentFieldsResponse `json:"expanded_document_fields"` + DocumentIDPhoto *DocumentIDPhotoResponse `json:"document_id_photo"` + textExtractionTasks []*TextExtractionTaskResponse +} + +// TextExtractionTasks returns a slice of text extraction tasks associated with the ID document +func (i *IDDocumentResourceResponse) TextExtractionTasks() []*TextExtractionTaskResponse { + return i.textExtractionTasks +} + +// UnmarshalJSON handles the custom JSON unmarshalling +func (i *IDDocumentResourceResponse) UnmarshalJSON(data []byte) error { + type result IDDocumentResourceResponse // declared as "type" to prevent recursive unmarshalling + if err := json.Unmarshal(data, (*result)(i)); err != nil { + return err + } + + for _, task := range i.Tasks { + switch task.Type { + case constants.IDDocumentTextDataExtraction: + i.textExtractionTasks = append(i.textExtractionTasks, &TextExtractionTaskResponse{TaskResponse: task}) + } + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response_test.go new file mode 100644 index 0000000..5554251 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_resource_response_test.go @@ -0,0 +1,44 @@ +package retrieve + +import ( + "encoding/json" + "testing" + + "gotest.tools/v3/assert" +) + +func TestIDDocumentResourceResponse_UnmarshalJSON(t *testing.T) { + idDocumentResource := &IDDocumentResourceResponse{ + ResourceResponse: &ResourceResponse{ + ID: "", + Tasks: []*TaskResponse{ + { + ID: "some-id", + Type: "ID_DOCUMENT_TEXT_DATA_EXTRACTION", + }, + { + ID: "other-id", + Type: "OTHER_TASK_TYPE", + }, + }, + }, + } + + marshalledResource, err := json.Marshal(idDocumentResource) + assert.NilError(t, err) + + var result IDDocumentResourceResponse + err = json.Unmarshal(marshalledResource, &result) + assert.NilError(t, err) + + assert.Equal(t, 2, len(result.ResourceResponse.Tasks)) + + assert.Equal(t, 1, len(result.TextExtractionTasks())) + assert.Equal(t, "some-id", result.TextExtractionTasks()[0].ID) +} + +func TestIDDocumentResourceResponse_UnmarshalJSON_Invalid(t *testing.T) { + var result IDDocumentResourceResponse + err := result.UnmarshalJSON([]byte("some-invalid-json")) + assert.ErrorContains(t, err, "invalid character") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response.go new file mode 100644 index 0000000..e4124c1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response.go @@ -0,0 +1,11 @@ +package retrieve + +// TextExtractionTaskResponse represents a Text Extraction task response +type TextExtractionTaskResponse struct { + *TaskResponse +} + +// GeneratedTextDataChecks filters the checks, returning only text data checks +func (t *TextExtractionTaskResponse) GeneratedTextDataChecks() []*GeneratedTextDataCheckResponse { + return t.generatedTextDataChecks +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response_test.go new file mode 100644 index 0000000..95066a4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/id_document_text_extraction_task_response_test.go @@ -0,0 +1,30 @@ +package retrieve + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + "gotest.tools/v3/assert" +) + +func TestTextExtractionTaskResponse_GeneratedTextDataChecks(t *testing.T) { + var checks []*GeneratedTextDataCheckResponse + checks = append( + checks, + &GeneratedTextDataCheckResponse{ + &GeneratedCheckResponse{ + Type: constants.IDDocumentTextDataCheck, + ID: "some-id", + }, + }, + ) + + taskResponse := &TextExtractionTaskResponse{ + TaskResponse: &TaskResponse{ + generatedTextDataChecks: checks, + }, + } + + assert.Equal(t, 1, len(taskResponse.GeneratedTextDataChecks())) + assert.Equal(t, "some-id", taskResponse.GeneratedTextDataChecks()[0].ID) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_preview.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_preview.go new file mode 100644 index 0000000..258f74b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_preview.go @@ -0,0 +1,6 @@ +package retrieve + +// IdentityProfilePreview contains info about the media needed to retrieve the Identity Profile Preview. +type IdentityProfilePreview struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_response.go new file mode 100644 index 0000000..ebfe0d8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/identity_profile_response.go @@ -0,0 +1,10 @@ +package retrieve + +// IdentityProfileResponse contains the SubjectId, teh Result/FailureReasonResponse, verified identity details, +// and the verification report that certifies how the identity was verified and how the verification level was achieved. +type IdentityProfileResponse struct { + SubjectId string `json:"subject_id"` + Result string `json:"result"` + FailureReasonResponse FailureReasonResponse `json:"failure_reason"` + Report map[string]interface{} `json:"identity_profile_report"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/import_token.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/import_token.go new file mode 100644 index 0000000..a6f7bc1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/import_token.go @@ -0,0 +1,7 @@ +package retrieve + +// ImportTokenResponse contains info about the media needed to retrieve the ImportToken. +type ImportTokenResponse struct { + FailureReason string `json:"failure_reason"` + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/liveness_resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/liveness_resource_response.go new file mode 100644 index 0000000..91101b4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/liveness_resource_response.go @@ -0,0 +1,7 @@ +package retrieve + +// LivenessResourceResponse represents a Liveness resource for a given session +type LivenessResourceResponse struct { + *ResourceResponse + LivenessType string `json:"liveness_type"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/media_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/media_response.go new file mode 100644 index 0000000..972c51c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/media_response.go @@ -0,0 +1,11 @@ +package retrieve + +import "time" + +// MediaResponse represents a media resource +type MediaResponse struct { + ID string `json:"id"` + Type string `json:"type"` + Created *time.Time `json:"created"` + LastUpdated *time.Time `json:"last_updated"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/page_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/page_response.go new file mode 100644 index 0000000..4ea4847 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/page_response.go @@ -0,0 +1,8 @@ +package retrieve + +// PageResponse represents information about an uploaded document Page +type PageResponse struct { + CaptureMethod string `json:"capture_method"` + Media *MediaResponse `json:"media"` + Frames []*FrameResponse `json:"frames"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/recommendation_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/recommendation_response.go new file mode 100644 index 0000000..c3ac3cf --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/recommendation_response.go @@ -0,0 +1,8 @@ +package retrieve + +// RecommendationResponse represents the recommendation given for a check +type RecommendationResponse struct { + Value string `json:"value"` + Reason string `json:"reason"` + RecoverySuggestion string `json:"recovery_suggestion"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/report_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/report_response.go new file mode 100644 index 0000000..96af2e2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/report_response.go @@ -0,0 +1,8 @@ +package retrieve + +// ReportResponse represents a report for a given check +type ReportResponse struct { + Recommendation RecommendationResponse `json:"recommendation"` + Breakdown []BreakdownResponse `json:"breakdown"` + WatchlistSummary *WatchlistSummaryResponse `json:"watchlist_summary"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource.go new file mode 100644 index 0000000..6862ea1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource.go @@ -0,0 +1,23 @@ +package retrieve + +import "fmt" + +type RequiredResourceResponse interface { + GetType() string + String() string +} + +type BaseRequiredResource struct { + Type string `json:"type"` + ID string `json:"id"` + State string `json:"state"` + LivenessType string `json:"liveness_type,omitempty"` +} + +func (b *BaseRequiredResource) GetType() string { + return b.Type +} + +func (b *BaseRequiredResource) String() string { + return fmt.Sprintf("Type: %s, ID: %s, State: %s", b.Type, b.ID, b.State) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource_test.go new file mode 100644 index 0000000..2b35897 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resource_test.go @@ -0,0 +1,73 @@ +package retrieve + +import ( + "gotest.tools/v3/assert" + "testing" +) + +func TestResource_Methods(t *testing.T) { + resources := []RequiredResourceResponse{ + &RequiredIdDocumentResourceResponse{ + BaseRequiredResource{ + Type: "ID_DOCUMENT", + ID: "id1", + State: "state1", + }, + }, + &RequiredSupplementaryDocumentResourceResponse{ + BaseRequiredResource{ + Type: "SUPPLEMENTARY_DOCUMENT", + ID: "id2", + State: "state2", + }, + }, + &RequiredZoomLivenessResourceResponse{ + BaseRequiredResource{ + Type: "LIVENESS", + ID: "id3", + State: "state3", + LivenessType: "ZOOM", + }, + }, + &RequiredStaticLivenessResourceResponse{ + BaseRequiredResource{ + Type: "LIVENESS", + ID: "id3", + State: "state3", + LivenessType: "STATIC", + }, + }, + &RequiredFaceCaptureResourceResponse{ + BaseRequiredResource{ + Type: "FACE_CAPTURE", + ID: "id4", + State: "state4", + }, + }, + &UnknownRequiredResourceResponse{ + BaseRequiredResource{ + Type: "UNKNOWN", + ID: "id5", + State: "state5", + }, + }, + } + + expectedTypes := []string{ + "ID_DOCUMENT", + "SUPPLEMENTARY_DOCUMENT", + "LIVENESS", + "LIVENESS", + "FACE_CAPTURE", + "UNKNOWN", + } + + for i, r := range resources { + // Test String() method + str := r.String() + assert.Assert(t, str != "", "String method should return a non-empty string for type %s", expectedTypes[i]) + + // Test GetType() method + assert.Equal(t, expectedTypes[i], r.GetType()) + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resources_types.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resources_types.go new file mode 100644 index 0000000..0ac803c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/required_resources_types.go @@ -0,0 +1,57 @@ +package retrieve + +type RequiredIdDocumentResourceResponse struct { + BaseRequiredResource +} + +func (r *RequiredIdDocumentResourceResponse) String() string { + return "ID Document Resource - " + r.BaseRequiredResource.String() +} + +type RequiredSupplementaryDocumentResourceResponse struct { + BaseRequiredResource +} + +func (r *RequiredSupplementaryDocumentResourceResponse) String() string { + return "Supplementary Document Resource - " + r.BaseRequiredResource.String() +} + +type RequiredZoomLivenessResourceResponse struct { + BaseRequiredResource +} + +func (r *RequiredZoomLivenessResourceResponse) String() string { + return "Zoom Liveness Resource - " + r.BaseRequiredResource.String() +} + +type RequiredLivenessResourceResponse struct { + BaseRequiredResource +} + +func (r *RequiredLivenessResourceResponse) String() string { + return "Liveness Resource - " + r.BaseRequiredResource.String() +} + +type RequiredStaticLivenessResourceResponse struct { + BaseRequiredResource +} + +func (r *RequiredStaticLivenessResourceResponse) String() string { + return "Static Liveness Resource - " + r.BaseRequiredResource.String() +} + +type RequiredFaceCaptureResourceResponse struct { + BaseRequiredResource +} + +func (r *RequiredFaceCaptureResourceResponse) String() string { + return "Face Capture Resource - " + r.BaseRequiredResource.String() +} + +type UnknownRequiredResourceResponse struct { + BaseRequiredResource +} + +func (r *UnknownRequiredResourceResponse) String() string { + return "Unknown Resource Type - " + r.BaseRequiredResource.String() +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container.go new file mode 100644 index 0000000..1906949 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container.go @@ -0,0 +1,70 @@ +package retrieve + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// ResourceContainer contains different resources that are part of the Yoti IDV session +type ResourceContainer struct { + IDDocuments []*IDDocumentResourceResponse `json:"id_documents"` + SupplementaryDocuments []*SupplementaryDocumentResourceResponse `json:"supplementary_documents"` + LivenessCapture []*LivenessResourceResponse + RawLivenessCapture []json.RawMessage `json:"liveness_capture"` + zoomLivenessResources []*ZoomLivenessResourceResponse + staticLivenessResources []*StaticLivenessResourceResponse +} + +// ZoomLivenessResources filters the liveness resources, returning only the "Zoom" liveness resources +func (r *ResourceContainer) ZoomLivenessResources() []*ZoomLivenessResourceResponse { + return r.zoomLivenessResources +} + +// ZoomLivenessResources filters the liveness resources, returning only the "Zoom" liveness resources +func (r *ResourceContainer) StaticLivenessResources() []*StaticLivenessResourceResponse { + return r.staticLivenessResources +} + +// UnmarshalJSON handles the custom JSON unmarshalling +func (r *ResourceContainer) UnmarshalJSON(data []byte) error { + type resourceContainer ResourceContainer + err := json.Unmarshal(data, (*resourceContainer)(r)) + if err != nil { + return err + } + + for _, raw := range r.RawLivenessCapture { + var v LivenessResourceResponse + err = json.Unmarshal(raw, &v) + if err != nil { + return err + } + + switch v.LivenessType { + case constants.Zoom: + var zoom ZoomLivenessResourceResponse + err = json.Unmarshal(raw, &zoom) + if err != nil { + return err + } + r.zoomLivenessResources = append(r.zoomLivenessResources, &zoom) + case constants.Static: + var static StaticLivenessResourceResponse + err = json.Unmarshal(raw, &static) + if err != nil { + return err + } + r.staticLivenessResources = append(r.staticLivenessResources, &static) + default: + err = json.Unmarshal(raw, &LivenessResourceResponse{}) + if err != nil { + return err + } + } + + r.LivenessCapture = append(r.LivenessCapture, &v) + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container_test.go new file mode 100644 index 0000000..da68408 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_container_test.go @@ -0,0 +1,43 @@ +package retrieve + +import ( + "encoding/json" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/file" + "gotest.tools/v3/assert" +) + +func TestLivenessResourceResponse_UnmarshalJSON(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/resource-container.json") + assert.NilError(t, err) + + var result ResourceContainer + err = json.Unmarshal(bytes, &result) + assert.NilError(t, err) + + assert.Equal(t, 2, len(result.LivenessCapture)) + assert.Equal(t, "ZOOM", result.LivenessCapture[0].LivenessType) + assert.Equal(t, "OTHER_LIVENESS_TYPE", result.LivenessCapture[1].LivenessType) + + assert.Equal(t, "IMAGE", result.ZoomLivenessResources()[0].Frames[0].Media.Type) + assert.Equal(t, "BINARY", result.ZoomLivenessResources()[0].FaceMap.Media.Type) +} + +func TestStaticLivenessResourceResponse_UnmarshalJSON(t *testing.T) { + bytes, err := file.ReadFile("../../../test/fixtures/resource-container-static.json") + assert.NilError(t, err) + + var result ResourceContainer + err = json.Unmarshal(bytes, &result) + assert.NilError(t, err) + + assert.Equal(t, 3, len(result.LivenessCapture)) + assert.Equal(t, "STATIC", result.LivenessCapture[0].LivenessType) +} + +func TestLivenessResourceResponse_UnmarshalJSON_Invalid(t *testing.T) { + var result ResourceContainer + err := result.UnmarshalJSON([]byte("some-invalid-json")) + assert.ErrorContains(t, err, "invalid character") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_response.go new file mode 100644 index 0000000..9e887f4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/resource_response.go @@ -0,0 +1,7 @@ +package retrieve + +// ResourceResponse represents a resource, with associated tasks +type ResourceResponse struct { + ID string `json:"id"` + Tasks []*TaskResponse `json:"tasks"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/search_config.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/search_config.go new file mode 100644 index 0000000..fd74d71 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/search_config.go @@ -0,0 +1,20 @@ +package retrieve + +type SearchConfig struct { + Type string `json:"type"` + Categories []string `json:"categories"` + RemoveDeceased bool `json:"remove_deceased"` + ShareURL bool `json:"share_url"` + Sources CASourcesResponse `json:"sources"` + MatchingStrategy CAMatchingStrategyResponse `json:"matching_strategy"` + APIKey string `json:"api_key"` + Monitoring bool `json:"monitoring"` + Tags map[string]string `json:"tags"` + ClientRef string `json:"client_ref"` +} + +type CAMatchingStrategyResponse struct { + Type string `json:"type"` + ExactMatch string `json:"exact_match"` + Fuzziness float64 `json:"fuzziness"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response.go new file mode 100644 index 0000000..9dc54ac --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response.go @@ -0,0 +1,52 @@ +package retrieve + +import ( + "encoding/json" + "errors" +) + +type SessionConfigurationResponse struct { + ClientSessionTokenTtl int `json:"client_session_token_ttl"` + SessionId string `json:"session_id"` + RequestedChecks []string `json:"requested_checks"` + Capture *CaptureResponse `json:"capture"` +} + +// NewSessionConfigurationResponse creates a new SessionConfigurationResponse from JSON payload bytes, +// validating mandatory fields. +func NewSessionConfigurationResponse(payload []byte) (*SessionConfigurationResponse, error) { + var resp SessionConfigurationResponse + if err := json.Unmarshal(payload, &resp); err != nil { + return nil, err + } + + if resp.ClientSessionTokenTtl <= 0 { + return nil, errors.New("client_session_token_ttl must be a positive integer") + } + if resp.SessionId == "" { + return nil, errors.New("session_id must be a non-empty string") + } + if resp.RequestedChecks == nil || len(resp.RequestedChecks) == 0 { + return nil, errors.New("requested_checks must be a non-empty array") + } + + return &resp, nil +} + +// Getter methods for each field + +func (r *SessionConfigurationResponse) GetClientSessionTokenTtl() int { + return r.ClientSessionTokenTtl +} + +func (r *SessionConfigurationResponse) GetSessionId() string { + return r.SessionId +} + +func (r *SessionConfigurationResponse) GetRequestedChecks() []string { + return r.RequestedChecks +} + +func (r *SessionConfigurationResponse) GetCapture() *CaptureResponse { + return r.Capture +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response_test.go new file mode 100644 index 0000000..8e79753 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/session_configuration_response_test.go @@ -0,0 +1,74 @@ +package retrieve + +import ( + "encoding/json" + "testing" + + "gotest.tools/v3/assert" +) + +func TestNewSessionConfigurationResponse_Success(t *testing.T) { + payload := SessionConfigurationResponse{ + ClientSessionTokenTtl: 3600, + SessionId: "abc123", + RequestedChecks: []string{"ID_DOCUMENT"}, + Capture: &CaptureResponse{}, // assuming zero value is acceptable + } + + data, err := json.Marshal(payload) + assert.NilError(t, err) + + result, err := NewSessionConfigurationResponse(data) + assert.NilError(t, err) + assert.Equal(t, result.ClientSessionTokenTtl, 3600) + assert.Equal(t, result.SessionId, "abc123") + assert.DeepEqual(t, result.RequestedChecks, []string{"ID_DOCUMENT"}) + assert.Assert(t, result.Capture != nil) +} + +func TestNewSessionConfigurationResponse_MissingTTL(t *testing.T) { + jsonData := `{ + "session_id": "abc123", + "requested_checks": ["ID_DOCUMENT"], + "capture": {} + }` + + _, err := NewSessionConfigurationResponse([]byte(jsonData)) + assert.ErrorContains(t, err, "client_session_token_ttl must be a positive integer") +} + +func TestNewSessionConfigurationResponse_MissingSessionID(t *testing.T) { + jsonData := `{ + "client_session_token_ttl": 3600, + "requested_checks": ["ID_DOCUMENT"], + "capture": {} + }` + + _, err := NewSessionConfigurationResponse([]byte(jsonData)) + assert.ErrorContains(t, err, "session_id must be a non-empty string") +} + +func TestNewSessionConfigurationResponse_MissingRequestedChecks(t *testing.T) { + jsonData := `{ + "client_session_token_ttl": 3600, + "session_id": "abc123", + "capture": {} + }` + + _, err := NewSessionConfigurationResponse([]byte(jsonData)) + assert.ErrorContains(t, err, "requested_checks must be a non-empty array") +} + +func TestSessionConfigurationResponse_Getters(t *testing.T) { + resp := &SessionConfigurationResponse{ + ClientSessionTokenTtl: 900, + SessionId: "test-session", + RequestedChecks: []string{"FACE_CAPTURE"}, + Capture: &CaptureResponse{}, + } + + assert.Equal(t, resp.GetClientSessionTokenTtl(), 900) + assert.Equal(t, resp.GetSessionId(), "test-session") + assert.DeepEqual(t, resp.GetRequestedChecks(), []string{"FACE_CAPTURE"}) + assert.Assert(t, resp.GetCapture() != nil) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/static_liveness_resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/static_liveness_resource_response.go new file mode 100644 index 0000000..d4c5061 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/static_liveness_resource_response.go @@ -0,0 +1,11 @@ +package retrieve + +// StaticLivenessResourceResponse represents a Static Liveness resource for a given session +type StaticLivenessResourceResponse struct { + *LivenessResourceResponse + Image *Image `json:"image"` +} + +type Image struct { + Media *MediaResponse `json:"media"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response.go new file mode 100644 index 0000000..1ab6383 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response.go @@ -0,0 +1,50 @@ +package retrieve + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// SupplementaryDocumentResourceResponse represents an supplementary document resource for a given session +type SupplementaryDocumentResourceResponse struct { + *ResourceResponse + // DocumentType is the supplementary document type, e.g. "UTILITY_BILL" + DocumentType string `json:"document_type"` + // IssuingCountry is the issuing country of the supplementary document + IssuingCountry string `json:"issuing_country"` + // Pages are the individual pages of the supplementary document + Pages []*PageResponse `json:"pages"` + // DocumentFields are the associated document fields of a document + DocumentFields *DocumentFieldsResponse `json:"document_fields"` + // DocumentFile is the associated document file + DocumentFile *FileResponse `json:"file"` + textExtractionTasks []*SupplementaryDocumentTextExtractionTaskResponse +} + +// TextExtractionTasks returns a slice of text extraction tasks associated with the supplementary document +func (i *SupplementaryDocumentResourceResponse) TextExtractionTasks() []*SupplementaryDocumentTextExtractionTaskResponse { + return i.textExtractionTasks +} + +// UnmarshalJSON handles the custom JSON unmarshalling +func (i *SupplementaryDocumentResourceResponse) UnmarshalJSON(data []byte) error { + type result SupplementaryDocumentResourceResponse // declared as "type" to prevent recursive unmarshalling + if err := json.Unmarshal(data, (*result)(i)); err != nil { + return err + } + + for _, task := range i.Tasks { + switch task.Type { + case constants.SupplementaryDocumentTextDataExtraction: + i.textExtractionTasks = append( + i.textExtractionTasks, + &SupplementaryDocumentTextExtractionTaskResponse{ + TaskResponse: task, + }, + ) + } + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response_test.go new file mode 100644 index 0000000..c04e321 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_resource_response_test.go @@ -0,0 +1,44 @@ +package retrieve + +import ( + "encoding/json" + "testing" + + "gotest.tools/v3/assert" +) + +func TestSupplementaryDocumentResourceResponse_UnmarshalJSON(t *testing.T) { + idDocumentResource := &SupplementaryDocumentResourceResponse{ + ResourceResponse: &ResourceResponse{ + ID: "", + Tasks: []*TaskResponse{ + { + ID: "some-id", + Type: "SUPPLEMENTARY_DOCUMENT_TEXT_DATA_EXTRACTION", + }, + { + ID: "other-id", + Type: "OTHER_TASK_TYPE", + }, + }, + }, + } + + marshalledResource, err := json.Marshal(idDocumentResource) + assert.NilError(t, err) + + var result SupplementaryDocumentResourceResponse + err = json.Unmarshal(marshalledResource, &result) + assert.NilError(t, err) + + assert.Equal(t, 2, len(result.ResourceResponse.Tasks)) + + assert.Equal(t, 1, len(result.TextExtractionTasks())) + assert.Equal(t, "some-id", result.TextExtractionTasks()[0].ID) +} + +func TestSupplementaryDocumentResourceResponse_UnmarshalJSON_Invalid(t *testing.T) { + var result SupplementaryDocumentResourceResponse + err := result.UnmarshalJSON([]byte("some-invalid-json")) + assert.ErrorContains(t, err, "invalid character") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response.go new file mode 100644 index 0000000..c6817c0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response.go @@ -0,0 +1,11 @@ +package retrieve + +// SupplementaryDocumentTextExtractionTaskResponse represents a Supplementary Document Text Extraction task response +type SupplementaryDocumentTextExtractionTaskResponse struct { + *TaskResponse +} + +// GeneratedTextDataChecks filters the checks, returning only text data checks +func (t *SupplementaryDocumentTextExtractionTaskResponse) GeneratedTextDataChecks() []*GeneratedSupplementaryTextDataCheckResponse { + return t.generatedSupplementaryTextDataChecks +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response_test.go new file mode 100644 index 0000000..8370a82 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/supplementary_document_text_extraction_task_response_test.go @@ -0,0 +1,28 @@ +package retrieve + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestSupplementaryDocumentTextExtractionTaskResponse_GeneratedTextDataChecks(t *testing.T) { + var checks []*GeneratedSupplementaryTextDataCheckResponse + checks = append( + checks, + &GeneratedSupplementaryTextDataCheckResponse{ + &GeneratedCheckResponse{ + ID: "some-id", + }, + }, + ) + + taskResponse := &SupplementaryDocumentTextExtractionTaskResponse{ + TaskResponse: &TaskResponse{ + generatedSupplementaryTextDataChecks: checks, + }, + } + + assert.Equal(t, 1, len(taskResponse.GeneratedTextDataChecks())) + assert.Equal(t, "some-id", taskResponse.GeneratedTextDataChecks()[0].ID) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response.go new file mode 100644 index 0000000..ca4e779 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response.go @@ -0,0 +1,56 @@ +package retrieve + +import ( + "encoding/json" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" +) + +// TaskResponse represents the attributes of a task, for any given session +type TaskResponse struct { + ID string `json:"id"` + Type string `json:"type"` + State string `json:"state"` + Created *time.Time `json:"created"` + LastUpdated *time.Time `json:"last_updated"` + GeneratedChecks []*GeneratedCheckResponse `json:"generated_checks"` + GeneratedMedia []*GeneratedMedia `json:"generated_media"` + generatedTextDataChecks []*GeneratedTextDataCheckResponse + generatedSupplementaryTextDataChecks []*GeneratedSupplementaryTextDataCheckResponse +} + +// GeneratedTextDataChecks filters the checks, returning only text data checks +// Deprecated: this function is now implemented on specific task types +func (t *TaskResponse) GeneratedTextDataChecks() []*GeneratedTextDataCheckResponse { + return t.generatedTextDataChecks +} + +// UnmarshalJSON handles the custom JSON unmarshalling +func (t *TaskResponse) UnmarshalJSON(data []byte) error { + type result TaskResponse // declared as "type" to prevent recursive unmarshalling + if err := json.Unmarshal(data, (*result)(t)); err != nil { + return err + } + + for _, check := range t.GeneratedChecks { + switch check.Type { + case constants.IDDocumentTextDataCheck: + t.generatedTextDataChecks = append( + t.generatedTextDataChecks, + &GeneratedTextDataCheckResponse{ + GeneratedCheckResponse: check, + }, + ) + case constants.SupplementaryDocumentTextDataCheck: + t.generatedSupplementaryTextDataChecks = append( + t.generatedSupplementaryTextDataChecks, + &GeneratedSupplementaryTextDataCheckResponse{ + GeneratedCheckResponse: check, + }, + ) + } + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response_test.go new file mode 100644 index 0000000..fb5f6cf --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/task_response_test.go @@ -0,0 +1,51 @@ +package retrieve + +import ( + "encoding/json" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/docscan/constants" + "gotest.tools/v3/assert" +) + +func TestTaskResponse_UnmarshalJSON(t *testing.T) { + checks := []*GeneratedCheckResponse{ + { + Type: constants.IDDocumentTextDataCheck, + ID: "some-id", + }, + { + Type: constants.SupplementaryDocumentTextDataCheck, + ID: "supplementary-id", + }, + { + Type: "OTHER_TYPE", + ID: "other-id", + }, + } + + taskResponse := TaskResponse{ + GeneratedChecks: checks, + } + marshalled, err := json.Marshal(&taskResponse) + assert.NilError(t, err) + + var result TaskResponse + err = json.Unmarshal(marshalled, &result) + assert.NilError(t, err) + + assert.Equal(t, 1, len(result.GeneratedTextDataChecks())) + assert.Equal(t, "some-id", result.GeneratedTextDataChecks()[0].ID) + + assert.Equal(t, 1, len(result.generatedTextDataChecks)) + assert.Equal(t, "some-id", result.generatedTextDataChecks[0].ID) + + assert.Equal(t, 1, len(result.generatedSupplementaryTextDataChecks)) + assert.Equal(t, "supplementary-id", result.generatedSupplementaryTextDataChecks[0].ID) +} + +func TestTaskResponse_UnmarshalJSON_Invalid(t *testing.T) { + var result TaskResponse + err := result.UnmarshalJSON([]byte("some-invalid-json")) + assert.ErrorContains(t, err, "invalid character") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/watchlist.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/watchlist.go new file mode 100644 index 0000000..4ce123f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/watchlist.go @@ -0,0 +1,12 @@ +package retrieve + +type RawResults struct { + Media MediaResponse `json:"media"` +} + +type WatchlistSummaryResponse struct { + TotalHits int `json:"total_hits"` + RawResults RawResults `json:"raw_results"` + AssociatedCountryCodes []string `json:"associated_country_codes"` + SearchConfig SearchConfig `json:"search_config"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/zoom_liveness_resource_response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/zoom_liveness_resource_response.go new file mode 100644 index 0000000..e9276eb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/session/retrieve/zoom_liveness_resource_response.go @@ -0,0 +1,8 @@ +package retrieve + +// ZoomLivenessResourceResponse represents a Zoom Liveness resource for a given session +type ZoomLivenessResourceResponse struct { + *LivenessResourceResponse + FaceMap *FaceMapResponse `json:"facemap"` + Frames []*FrameResponse `json:"frames"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/supported/supported_documents.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/supported/supported_documents.go new file mode 100644 index 0000000..bce18e4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/docscan/supported/supported_documents.go @@ -0,0 +1,18 @@ +package supported + +// DocumentsResponse details the supported countries and associated documents +type DocumentsResponse struct { + SupportedCountries []*Country `json:"supported_countries"` +} + +// Country details the supported documents for a particular country +type Country struct { + Code string `json:"code"` + SupportedDocuments []*Document `json:"supported_documents"` +} + +// Document is the document type that is supported +type Document struct { + Type string `json:"type"` + IsStrictlyLatin bool `json:"is_strictly_latin"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder.go new file mode 100644 index 0000000..e7f8930 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder.go @@ -0,0 +1,261 @@ +package dynamic + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +const ( + authTypeSelfieConst = 1 + authTypePinConst = 2 +) + +// PolicyBuilder constructs a json payload specifying the dynamic policy +// for a dynamic scenario +type PolicyBuilder struct { + wantedAttributes map[string]WantedAttribute + wantedAuthTypes map[int]bool + isWantedRememberMe bool + err error + identityProfileRequirements *json.RawMessage + advancedIdentityProfileRequirements *json.RawMessage +} + +// Policy represents a dynamic policy for a share +type Policy struct { + attributes []WantedAttribute + authTypes []int + rememberMeID bool + identityProfileRequirements *json.RawMessage + advancedIdentityProfileRequirements *json.RawMessage +} + +// WithWantedAttribute adds an attribute from WantedAttributeBuilder to the policy +func (b *PolicyBuilder) WithWantedAttribute(attribute WantedAttribute) *PolicyBuilder { + if b.wantedAttributes == nil { + b.wantedAttributes = make(map[string]WantedAttribute) + } + var key string + if attribute.derivation != "" { + key = attribute.derivation + } else { + key = attribute.name + } + b.wantedAttributes[key] = attribute + return b +} + +// WithWantedAttributeByName adds an attribute by its name. This is not the preferred +// way of adding an attribute - instead use the other methods below. +// Options allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithWantedAttributeByName(name string, options ...interface{}) *PolicyBuilder { + attributeBuilder := (&WantedAttributeBuilder{}).WithName(name) + + for _, option := range options { + switch value := option.(type) { + case SourceConstraint: + attributeBuilder.WithConstraint(&value) + case constraintInterface: + attributeBuilder.WithConstraint(value) + default: + panic(fmt.Sprintf("not a valid option type, %v", value)) + } + } + + attribute, err := attributeBuilder.Build() + if err != nil { + b.err = yotierror.MultiError{This: err, Next: b.err} + } + b.WithWantedAttribute(attribute) + return b +} + +// WithFamilyName adds the family name attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithFamilyName(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrFamilyName, options...) +} + +// WithGivenNames adds the given names attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithGivenNames(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrGivenNames, options...) +} + +// WithFullName adds the full name attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithFullName(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrFullName, options...) +} + +// WithDateOfBirth adds the date of birth attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithDateOfBirth(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrDateOfBirth, options...) +} + +// WithGender adds the gender attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithGender(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrGender, options...) +} + +// WithPostalAddress adds the postal address attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithPostalAddress(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrAddress, options...) +} + +// WithStructuredPostalAddress adds the structured postal address attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithStructuredPostalAddress(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrStructuredPostalAddress, options...) +} + +// WithNationality adds the nationality attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithNationality(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrNationality, options...) +} + +// WithPhoneNumber adds the phone number attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithPhoneNumber(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrMobileNumber, options...) +} + +// WithSelfie adds the selfie attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithSelfie(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrSelfie, options...) +} + +// WithEmail adds the email address attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithEmail(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrEmailAddress, options...) +} + +// WithDocumentImages adds the document images attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithDocumentImages(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrDocumentImages, options...) +} + +// WithDocumentDetails adds the document details attribute, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithDocumentDetails(options ...interface{}) *PolicyBuilder { + return b.WithWantedAttributeByName(consts.AttrDocumentDetails, options...) +} + +// WithAgeDerivedAttribute is a helper method for setting age based derivations +// Prefer to use WithAgeOver and WithAgeUnder instead of using this directly. +// "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithAgeDerivedAttribute(derivation string, options ...interface{}) *PolicyBuilder { + var attributeBuilder WantedAttributeBuilder + attributeBuilder. + WithName(consts.AttrDateOfBirth). + WithDerivation(derivation) + + for _, option := range options { + switch value := option.(type) { + case SourceConstraint: + attributeBuilder.WithConstraint(&value) + case constraintInterface: + attributeBuilder.WithConstraint(value) + default: + panic(fmt.Sprintf("not a valid option type, %v", value)) + } + } + + attr, err := attributeBuilder.Build() + if err != nil { + b.err = yotierror.MultiError{This: err, Next: b.err} + } + return b.WithWantedAttribute(attr) +} + +// WithAgeOver sets this dynamic policy as requesting whether the user is older than a certain age. +// "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithAgeOver(age int, options ...interface{}) *PolicyBuilder { + return b.WithAgeDerivedAttribute(fmt.Sprintf(consts.AttrAgeOver, age), options...) +} + +// WithAgeUnder sets this dynamic policy as requesting whether the user is younger +// than a certain age, "options" allows one or more options to be specified e.g. SourceConstraint +func (b *PolicyBuilder) WithAgeUnder(age int, options ...interface{}) *PolicyBuilder { + return b.WithAgeDerivedAttribute(fmt.Sprintf(consts.AttrAgeUnder, age), options...) +} + +// WithWantedRememberMe sets the Policy as requiring a "Remember Me ID" +func (b *PolicyBuilder) WithWantedRememberMe() *PolicyBuilder { + b.isWantedRememberMe = true + return b +} + +// WithWantedAuthType sets this dynamic policy as requiring a specific authentication type +func (b *PolicyBuilder) WithWantedAuthType(wantedAuthType int) *PolicyBuilder { + if b.wantedAuthTypes == nil { + b.wantedAuthTypes = make(map[int]bool) + } + b.wantedAuthTypes[wantedAuthType] = true + return b +} + +// WithSelfieAuth sets this dynamic policy as requiring Selfie-based authentication +func (b *PolicyBuilder) WithSelfieAuth() *PolicyBuilder { + return b.WithWantedAuthType(authTypeSelfieConst) +} + +// WithPinAuth sets this dynamic policy as requiring PIN authentication +func (b *PolicyBuilder) WithPinAuth() *PolicyBuilder { + return b.WithWantedAuthType(authTypePinConst) +} + +// WithIdentityProfileRequirements adds Identity Profile Requirements to the policy. Must be valid JSON. +func (b *PolicyBuilder) WithIdentityProfileRequirements(identityProfile json.RawMessage) *PolicyBuilder { + b.identityProfileRequirements = &identityProfile + return b +} + +// WithAdvancedIdentityProfileRequirements adds Advanced Identity Profile Requirements to the policy. Must be valid JSON. +func (b *PolicyBuilder) WithAdvancedIdentityProfileRequirements(advancedIdentityProfile json.RawMessage) *PolicyBuilder { + b.advancedIdentityProfileRequirements = &advancedIdentityProfile + return b +} + +// Build constructs a dynamic policy object +func (b *PolicyBuilder) Build() (Policy, error) { + return Policy{ + attributes: b.attributesAsList(), + authTypes: b.authTypesAsList(), + rememberMeID: b.isWantedRememberMe, + identityProfileRequirements: b.identityProfileRequirements, + advancedIdentityProfileRequirements: b.advancedIdentityProfileRequirements, + }, b.err +} + +func (b *PolicyBuilder) attributesAsList() []WantedAttribute { + attributeList := make([]WantedAttribute, 0) + for _, attr := range b.wantedAttributes { + attributeList = append(attributeList, attr) + } + return attributeList +} + +func (b *PolicyBuilder) authTypesAsList() []int { + authTypeList := make([]int, 0) + for auth, boolValue := range b.wantedAuthTypes { + if boolValue { + authTypeList = append(authTypeList, auth) + } + } + return authTypeList +} + +// MarshalJSON returns the JSON encoding +func (policy *Policy) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Wanted []WantedAttribute `json:"wanted"` + WantedAuthTypes []int `json:"wanted_auth_types"` + WantedRememberMe bool `json:"wanted_remember_me"` + IdentityProfileRequirements *json.RawMessage `json:"identity_profile_requirements,omitempty"` + AdvancedIdentityProfileRequirements *json.RawMessage `json:"advanced_identity_profile_requirements,omitempty"` + }{ + Wanted: policy.attributes, + WantedAuthTypes: policy.authTypes, + WantedRememberMe: policy.rememberMeID, + IdentityProfileRequirements: policy.identityProfileRequirements, + AdvancedIdentityProfileRequirements: policy.advancedIdentityProfileRequirements, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder_test.go new file mode 100644 index 0000000..7402e67 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/policy_builder_test.go @@ -0,0 +1,533 @@ +package dynamic + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" + "gotest.tools/v3/assert" +) + +func ExamplePolicyBuilder_WithFamilyName() { + policy, err := (&PolicyBuilder{}).WithFamilyName().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"family_name","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithDocumentDetails() { + policy, err := (&PolicyBuilder{}).WithDocumentDetails().Build() + if err != nil { + return + } + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"document_details","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithDocumentImages() { + policy, err := (&PolicyBuilder{}).WithDocumentImages().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"document_images","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithSelfie() { + policy, err := (&PolicyBuilder{}).WithSelfie().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"selfie","accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithAgeOver() { + constraint, err := (&SourceConstraintBuilder{}).WithDrivingLicence("").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + policy, err := (&PolicyBuilder{}).WithAgeOver(18, constraint).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.attributes[0].MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"name":"date_of_birth","derivation":"age_over:18","constraints":[{"type":"SOURCE","preferred_sources":{"anchors":[{"name":"DRIVING_LICENCE","sub_type":""}],"soft_preference":false}}],"accept_self_asserted":false} +} + +func ExamplePolicyBuilder_WithSelfieAuth() { + policy, err := (&PolicyBuilder{}).WithSelfieAuth().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[1],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithWantedRememberMe() { + policy, err := (&PolicyBuilder{}).WithWantedRememberMe().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[],"wanted_remember_me":true} +} + +func ExamplePolicyBuilder_WithFullName() { + constraint, err := (&SourceConstraintBuilder{}).WithPassport("").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + policy, err := (&PolicyBuilder{}).WithFullName(&constraint).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"wanted":[{"name":"full_name","constraints":[{"type":"SOURCE","preferred_sources":{"anchors":[{"name":"PASSPORT","sub_type":""}],"soft_preference":false}}],"accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder() { + policy, err := (&PolicyBuilder{}).WithFullName(). + WithPinAuth().WithWantedRememberMe().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"full_name","accept_self_asserted":false}],"wanted_auth_types":[2],"wanted_remember_me":true} +} + +func ExamplePolicyBuilder_WithAgeUnder() { + policy, err := (&PolicyBuilder{}).WithAgeUnder(18).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"date_of_birth","derivation":"age_under:18","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithGivenNames() { + policy, err := (&PolicyBuilder{}).WithGivenNames().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"given_names","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithDateOfBirth() { + policy, err := (&PolicyBuilder{}).WithDateOfBirth().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"date_of_birth","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithGender() { + policy, err := (&PolicyBuilder{}).WithGender().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"gender","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithPostalAddress() { + policy, err := (&PolicyBuilder{}).WithPostalAddress().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"postal_address","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithStructuredPostalAddress() { + policy, err := (&PolicyBuilder{}).WithStructuredPostalAddress().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"structured_postal_address","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithNationality() { + policy, err := (&PolicyBuilder{}).WithNationality().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"nationality","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func ExamplePolicyBuilder_WithPhoneNumber() { + policy, err := (&PolicyBuilder{}).WithPhoneNumber().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[{"name":"phone_number","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false} +} + +func TestDynamicPolicyBuilder_WithWantedAttributeByName_WithSourceConstraint(t *testing.T) { + attributeName := "attributeName" + builder := &PolicyBuilder{} + sourceConstraint, err := (&SourceConstraintBuilder{}).Build() + assert.NilError(t, err) + + builder.WithWantedAttributeByName( + attributeName, + sourceConstraint, + ) + + policy, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, len(policy.attributes), 1) + assert.Equal(t, policy.attributes[0].name, attributeName) + assert.Equal(t, len(policy.attributes[0].constraints), 1) +} + +func TestDynamicPolicyBuilder_WithWantedAttributeByName_InvalidOptionsShouldPanic(t *testing.T) { + attributeName := "attributeName" + builder := &PolicyBuilder{} + invalidOption := "invalidOption" + + defer func() { + r := recover().(string) + assert.Check(t, strings.Contains(r, "not a valid option type")) + }() + + builder.WithWantedAttributeByName( + attributeName, + invalidOption, + ) + + t.Error("Expected Panic") + +} + +func TestDynamicPolicyBuilder_WithWantedAttributeByName_ShouldPropagateErrors(t *testing.T) { + builder := &PolicyBuilder{} + + builder.WithWantedAttributeByName("") + builder.WithWantedAttributeByName("") + + _, err := builder.Build() + + assert.Error(t, err, "Wanted attribute names must not be empty, Wanted attribute names must not be empty") + assert.Error(t, err.(yotierror.MultiError).Unwrap(), "Wanted attribute names must not be empty") +} + +func TestDynamicPolicyBuilder_WithAgeDerivedAttribute_WithSourceConstraint(t *testing.T) { + builder := &PolicyBuilder{} + sourceConstraint, err := (&SourceConstraintBuilder{}).Build() + assert.NilError(t, err) + + builder.WithAgeDerivedAttribute( + fmt.Sprintf(consts.AttrAgeOver, 18), + sourceConstraint, + ) + + policy, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, len(policy.attributes), 1) + assert.Equal(t, policy.attributes[0].name, consts.AttrDateOfBirth) + assert.Equal(t, len(policy.attributes[0].constraints), 1) +} + +func TestDynamicPolicyBuilder_WithAgeDerivedAttribute_WithConstraintInterface(t *testing.T) { + builder := &PolicyBuilder{} + var constraint constraintInterface + sourceConstraint, err := (&SourceConstraintBuilder{}).Build() + constraint = &sourceConstraint + assert.NilError(t, err) + + builder.WithAgeDerivedAttribute( + fmt.Sprintf(consts.AttrAgeOver, 18), + constraint, + ) + + policy, err := builder.Build() + assert.NilError(t, err) + assert.Equal(t, len(policy.attributes), 1) + assert.Equal(t, policy.attributes[0].name, consts.AttrDateOfBirth) + assert.Equal(t, len(policy.attributes[0].constraints), 1) +} + +func TestDynamicPolicyBuilder_WithAgeDerivedAttribute_InvalidOptionsShouldPanic(t *testing.T) { + builder := &PolicyBuilder{} + invalidOption := "invalidOption" + + defer func() { + r := recover().(string) + assert.Check(t, strings.Contains(r, "not a valid option type")) + }() + + builder.WithAgeDerivedAttribute( + fmt.Sprintf(consts.AttrAgeOver, 18), + invalidOption, + ) + + t.Error("Expected Panic") + +} + +func ExamplePolicyBuilder_WithIdentityProfileRequirements() { + identityProfile := []byte(`{ + "trust_framework": "UK_TFIDA", + "scheme": { + "type": "DBS", + "objective": "STANDARD" + } + }`) + + policy, err := (&PolicyBuilder{}).WithIdentityProfileRequirements(identityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[],"wanted_remember_me":false,"identity_profile_requirements":{"trust_framework":"UK_TFIDA","scheme":{"type":"DBS","objective":"STANDARD"}}} +} + +func TestPolicyBuilder_WithIdentityProfileRequirements_ShouldFailForInvalidJSON(t *testing.T) { + identityProfile := []byte(`{ + "trust_framework": UK_TFIDA", + , + }`) + + policy, err := (&PolicyBuilder{}).WithIdentityProfileRequirements(identityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + _, err = policy.MarshalJSON() + if err == nil { + t.Error("expected an error") + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} + +func ExamplePolicyBuilder_WithAdvancedIdentityProfileRequirements() { + advancedIdentityProfile := []byte(`{ + "profiles": [ + { + "trust_framework": "UK_TFIDA", + "schemes": [ + { + "label": "LB912", + "type": "RTW" + }, + { + "label": "LB777", + "type": "DBS", + "objective": "BASIC" + } + ] + }, + { + "trust_framework": "YOTI_GLOBAL", + "schemes": [ + { + "label": "LB321", + "type": "IDENTITY", + "objective": "AL_L1", + "config": {} + } + ] + } + ] + }`) + + policy, err := (&PolicyBuilder{}).WithAdvancedIdentityProfileRequirements(advancedIdentityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := policy.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"wanted":[],"wanted_auth_types":[],"wanted_remember_me":false,"advanced_identity_profile_requirements":{"profiles":[{"trust_framework":"UK_TFIDA","schemes":[{"label":"LB912","type":"RTW"},{"label":"LB777","type":"DBS","objective":"BASIC"}]},{"trust_framework":"YOTI_GLOBAL","schemes":[{"label":"LB321","type":"IDENTITY","objective":"AL_L1","config":{}}]}]}} +} + +func TestPolicyBuilder_WithAdvancedIdentityProfileRequirements_ShouldFailForInvalidJSON(t *testing.T) { + advancedIdentityProfile := []byte(`{ + "trust_framework": UK_TFIDA", + , + }`) + + policy, err := (&PolicyBuilder{}).WithAdvancedIdentityProfileRequirements(advancedIdentityProfile).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + _, err = policy.MarshalJSON() + if err == nil { + t.Error("expected an error") + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder.go new file mode 100644 index 0000000..fdb4df4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder.go @@ -0,0 +1,73 @@ +package dynamic + +import ( + "encoding/json" +) + +// ScenarioBuilder builds a dynamic scenario +type ScenarioBuilder struct { + scenario Scenario + err error +} + +// Scenario represents a dynamic scenario +type Scenario struct { + policy *Policy + extensions []interface{} + callbackEndpoint string + subject *json.RawMessage +} + +// WithPolicy attaches a DynamicPolicy to the DynamicScenario +func (builder *ScenarioBuilder) WithPolicy(policy Policy) *ScenarioBuilder { + builder.scenario.policy = &policy + return builder +} + +// WithExtension adds an extension to the scenario +func (builder *ScenarioBuilder) WithExtension(extension interface{}) *ScenarioBuilder { + builder.scenario.extensions = append(builder.scenario.extensions, extension) + return builder +} + +// WithCallbackEndpoint sets the callback URL +func (builder *ScenarioBuilder) WithCallbackEndpoint(endpoint string) *ScenarioBuilder { + builder.scenario.callbackEndpoint = endpoint + return builder +} + +// WithSubject adds a subject to the scenario. Must be valid JSON. +func (builder *ScenarioBuilder) WithSubject(subject json.RawMessage) *ScenarioBuilder { + builder.scenario.subject = &subject + return builder +} + +// Build constructs the DynamicScenario +func (builder *ScenarioBuilder) Build() (Scenario, error) { + if builder.scenario.extensions == nil { + builder.scenario.extensions = make([]interface{}, 0) + } + if builder.scenario.policy == nil { + policy, err := (&PolicyBuilder{}).Build() + if err != nil { + return builder.scenario, err + } + builder.scenario.policy = &policy + } + return builder.scenario, builder.err +} + +// MarshalJSON returns the JSON encoding +func (scenario Scenario) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Policy Policy `json:"policy"` + Extensions []interface{} `json:"extensions"` + CallbackEndpoint string `json:"callback_endpoint"` + Subject *json.RawMessage `json:"subject,omitempty"` + }{ + Policy: *scenario.policy, + Extensions: scenario.extensions, + CallbackEndpoint: scenario.callbackEndpoint, + Subject: scenario.subject, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder_test.go new file mode 100644 index 0000000..cbef8cb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/scenario_builder_test.go @@ -0,0 +1,124 @@ +package dynamic + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/extension" +) + +func ExampleScenarioBuilder() { + scenario, err := (&ScenarioBuilder{}).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := scenario.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":[],"wanted_auth_types":[],"wanted_remember_me":false},"extensions":[],"callback_endpoint":""} +} + +func ExampleScenarioBuilder_WithPolicy() { + policy, err := (&PolicyBuilder{}).WithEmail().WithPinAuth().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + scenario, err := (&ScenarioBuilder{}).WithPolicy(policy).WithCallbackEndpoint("/foo").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := scenario.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":[{"name":"email_address","accept_self_asserted":false}],"wanted_auth_types":[2],"wanted_remember_me":false},"extensions":[],"callback_endpoint":"/foo"} +} + +func ExampleScenarioBuilder_WithExtension() { + policy, err := (&PolicyBuilder{}).WithFullName().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + builtExtension, err := (&extension.TransactionalFlowExtensionBuilder{}). + WithContent("Transactional Flow Extension"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + scenario, err := (&ScenarioBuilder{}).WithExtension(builtExtension).WithPolicy(policy).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := scenario.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":[{"name":"full_name","accept_self_asserted":false}],"wanted_auth_types":[],"wanted_remember_me":false},"extensions":[{"type":"TRANSACTIONAL_FLOW","content":"Transactional Flow Extension"}],"callback_endpoint":""} +} + +func ExampleScenarioBuilder_WithSubject() { + subject := []byte(`{ + "subject_id": "some_subject_id_string" + }`) + + scenario, err := (&ScenarioBuilder{}).WithSubject(subject).WithCallbackEndpoint("/foo").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := scenario.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"policy":{"wanted":[],"wanted_auth_types":[],"wanted_remember_me":false},"extensions":[],"callback_endpoint":"/foo","subject":{"subject_id":"some_subject_id_string"}} +} + +func TestScenarioBuilder_WithSubject_ShouldFailForInvalidJSON(t *testing.T) { + subject := []byte(`{ + subject_id: some_subject_id_string + }`) + + scenario, err := (&ScenarioBuilder{}).WithSubject(subject).WithCallbackEndpoint("/foo").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + _, err = scenario.MarshalJSON() + if err == nil { + t.Error("expected an error") + } + var marshallerErr *json.MarshalerError + if !errors.As(err, &marshallerErr) { + t.Errorf("wanted err to be of type '%v', got: '%v'", reflect.TypeOf(marshallerErr), reflect.TypeOf(err)) + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service.go new file mode 100644 index 0000000..ed44cdb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service.go @@ -0,0 +1,55 @@ +package dynamic + +import ( + "crypto/rsa" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/getyoti/yoti-go-sdk/v3/requests" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +func getDynamicShareEndpoint(clientSdkId string) string { + return fmt.Sprintf( + "/qrcodes/apps/%s", + clientSdkId, + ) +} + +// CreateShareURL creates a QR code for a dynamic scenario +func CreateShareURL(httpClient requests.HttpClient, scenario *Scenario, clientSdkId, apiUrl string, key *rsa.PrivateKey) (share ShareURL, err error) { + endpoint := getDynamicShareEndpoint(clientSdkId) + + payload, err := scenario.MarshalJSON() + if err != nil { + return + } + + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodPost, + BaseURL: apiUrl, + Endpoint: endpoint, + Headers: nil, + Body: payload, + }.Request() + if err != nil { + return + } + + response, err := requests.Execute(httpClient, request, ShareURLHTTPErrorMessages, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return share, err + } + + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return + } + + err = json.Unmarshal(responseBytes, &share) + + return +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service_test.go new file mode 100644 index 0000000..9057bc1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/service_test.go @@ -0,0 +1,115 @@ +package dynamic + +import ( + "fmt" + "io" + "net/http" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "gotest.tools/v3/assert" +) + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func ExampleCreateShareURL() { + key := test.GetValidKey("../test/test-key.pem") + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"qrcode":"https://code.yoti.com/CAEaJDQzNzllZDc0LTU0YjItNDkxMy04OTE4LTExYzM2ZDU2OTU3ZDAC","ref_id":"0"}`)), + }, nil + }, + } + + policy, err := (&PolicyBuilder{}).WithFullName().WithWantedRememberMe().Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + scenario, err := (&ScenarioBuilder{}).WithPolicy(policy).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + result, err := CreateShareURL(client, &scenario, "sdkId", "https://apiurl", key) + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Printf("QR code: %s", result.ShareURL) + // Output: QR code: https://code.yoti.com/CAEaJDQzNzllZDc0LTU0YjItNDkxMy04OTE4LTExYzM2ZDU2OTU3ZDAC +} + +func TestCreateShareURL_Unsuccessful_503(t *testing.T) { + _, err := createShareUrlWithErrorResponse(503, "some service unavailable response") + + assert.ErrorContains(t, err, "503: unknown HTTP error - some service unavailable response") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, temporary && tempError.Temporary()) +} + +func TestCreateShareURL_Unsuccessful_404(t *testing.T) { + _, err := createShareUrlWithErrorResponse(404, "some not found response") + + assert.ErrorContains(t, err, "404: Application was not found - some not found response") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestCreateShareURL_Unsuccessful_400(t *testing.T) { + _, err := createShareUrlWithErrorResponse(400, "some invalid JSON response") + + assert.ErrorContains(t, err, "400: JSON is incorrect, contains invalid data - some invalid JSON response") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func createShareUrlWithErrorResponse(statusCode int, responseBody string) (share ShareURL, err error) { + key := test.GetValidKey("../test/test-key.pem") + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: statusCode, + Body: io.NopCloser(strings.NewReader(responseBody)), + }, nil + }, + } + + policy, err := (&PolicyBuilder{}).WithFullName().WithWantedRememberMe().Build() + if err != nil { + return + } + scenario, err := (&ScenarioBuilder{}).WithPolicy(policy).Build() + if err != nil { + return + } + + return CreateShareURL(client, &scenario, "sdkId", "https://apiurl", key) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/share_url.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/share_url.go new file mode 100644 index 0000000..3b66507 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/share_url.go @@ -0,0 +1,16 @@ +package dynamic + +var ( + // ShareURLHTTPErrorMessages specifies the HTTP error status codes used + // by the Share URL API + ShareURLHTTPErrorMessages = map[int]string{ + 400: "JSON is incorrect, contains invalid data", + 404: "Application was not found", + } +) + +// ShareURL contains a dynamic share QR code +type ShareURL struct { + ShareURL string `json:"qrcode"` + RefID string `json:"ref_id"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint.go new file mode 100644 index 0000000..511b703 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint.go @@ -0,0 +1,105 @@ +package dynamic + +import ( + "encoding/json" + + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +// Anchor name constants +const ( + AnchorDrivingLicenceConst = "DRIVING_LICENCE" + AnchorPassportConst = "PASSPORT" + AnchorNationalIDConst = "NATIONAL_ID" + AnchorPassCardConst = "PASS_CARD" +) + +// SourceConstraint describes a requirement or preference for a particular set +// of anchors +type SourceConstraint struct { + anchors []WantedAnchor + softPreference bool +} + +// SourceConstraintBuilder builds a source constraint +type SourceConstraintBuilder struct { + sourceConstraint SourceConstraint + err error +} + +// WithAnchorByValue is a helper method which builds an anchor and adds it to +// the source constraint +func (b *SourceConstraintBuilder) WithAnchorByValue(value, subtype string) *SourceConstraintBuilder { + anchor, err := (&WantedAnchorBuilder{}). + WithValue(value). + WithSubType(subtype). + Build() + if err != nil { + b.err = yotierror.MultiError{This: err, Next: b.err} + } + + return b.WithAnchor(anchor) +} + +// WithAnchor adds an anchor to the preference list +func (b *SourceConstraintBuilder) WithAnchor(anchor WantedAnchor) *SourceConstraintBuilder { + b.sourceConstraint.anchors = append(b.sourceConstraint.anchors, anchor) + return b +} + +// WithPassport adds a passport anchor +func (b *SourceConstraintBuilder) WithPassport(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorPassportConst, subtype) +} + +// WithDrivingLicence adds a Driving Licence anchor +func (b *SourceConstraintBuilder) WithDrivingLicence(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorDrivingLicenceConst, subtype) +} + +// WithNationalID adds a national ID anchor +func (b *SourceConstraintBuilder) WithNationalID(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorNationalIDConst, subtype) +} + +// WithPasscard adds a passcard anchor +func (b *SourceConstraintBuilder) WithPasscard(subtype string) *SourceConstraintBuilder { + return b.WithAnchorByValue(AnchorPassCardConst, subtype) +} + +// WithSoftPreference sets this constraint as a 'soft requirement' if the +// parameter is true, and a hard requirement if it is false. +func (b *SourceConstraintBuilder) WithSoftPreference(soft bool) *SourceConstraintBuilder { + b.sourceConstraint.softPreference = soft + return b +} + +// Build builds a SourceConstraint +func (b *SourceConstraintBuilder) Build() (SourceConstraint, error) { + if b.sourceConstraint.anchors == nil { + b.sourceConstraint.anchors = make([]WantedAnchor, 0) + } + return b.sourceConstraint, b.err +} + +func (constraint *SourceConstraint) isConstraint() bool { + return true +} + +// MarshalJSON returns the JSON encoding +func (constraint *SourceConstraint) MarshalJSON() ([]byte, error) { + type PreferenceList struct { + Anchors []WantedAnchor `json:"anchors"` + SoftPreference bool `json:"soft_preference"` + } + return json.Marshal(&struct { + Type string `json:"type"` + PreferredSources PreferenceList `json:"preferred_sources"` + }{ + Type: "SOURCE", + PreferredSources: PreferenceList{ + Anchors: constraint.anchors, + SoftPreference: constraint.softPreference, + }, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint_builder_test.go new file mode 100644 index 0000000..6b8f39f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/source_constraint_builder_test.go @@ -0,0 +1,115 @@ +package dynamic + +import ( + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleSourceConstraint() { + drivingLicence, err := (&WantedAnchorBuilder{}).WithValue("DRIVING_LICENCE").Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + sourceConstraint, err := (&SourceConstraintBuilder{}). + WithAnchor(drivingLicence). + WithSoftPreference(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + json, err := sourceConstraint.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println("SourceConstraint:", string(json)) + // Output: SourceConstraint: {"type":"SOURCE","preferred_sources":{"anchors":[{"name":"DRIVING_LICENCE","sub_type":""}],"soft_preference":true}} +} + +func ExampleSourceConstraintBuilder_WithPassport() { + sourceConstraint, err := (&SourceConstraintBuilder{}). + WithPassport(""). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + json, err := sourceConstraint.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(json)) + // Output: {"type":"SOURCE","preferred_sources":{"anchors":[{"name":"PASSPORT","sub_type":""}],"soft_preference":false}} +} + +func ExampleSourceConstraintBuilder_WithDrivingLicence() { + sourceConstraint, err := (&SourceConstraintBuilder{}). + WithDrivingLicence(""). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + json, err := sourceConstraint.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(json)) + // Output: {"type":"SOURCE","preferred_sources":{"anchors":[{"name":"DRIVING_LICENCE","sub_type":""}],"soft_preference":false}} +} + +func ExampleSourceConstraintBuilder_WithNationalID() { + sourceConstraint, err := (&SourceConstraintBuilder{}). + WithNationalID(""). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + json, err := sourceConstraint.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(json)) + // Output: {"type":"SOURCE","preferred_sources":{"anchors":[{"name":"NATIONAL_ID","sub_type":""}],"soft_preference":false}} +} + +func ExampleSourceConstraintBuilder_WithPasscard() { + sourceConstraint, err := (&SourceConstraintBuilder{}). + WithPasscard(""). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + json, err := sourceConstraint.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(json)) + // Output: {"type":"SOURCE","preferred_sources":{"anchors":[{"name":"PASS_CARD","sub_type":""}],"soft_preference":false}} +} + +func TestSourceConstraint_isConstraintImplemented(t *testing.T) { + constraint := &SourceConstraint{} + assert.Check(t, constraint.isConstraint()) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder.go new file mode 100644 index 0000000..1a5c7b6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder.go @@ -0,0 +1,44 @@ +package dynamic + +import ( + "encoding/json" +) + +// WantedAnchor specifies a preferred anchor for a user's details +type WantedAnchor struct { + name string + subType string +} + +// WantedAnchorBuilder describes a desired anchor for user profile data +type WantedAnchorBuilder struct { + wantedAnchor WantedAnchor +} + +// WithValue sets the anchor's name +func (b *WantedAnchorBuilder) WithValue(name string) *WantedAnchorBuilder { + b.wantedAnchor.name = name + return b +} + +// WithSubType sets the anchors subtype +func (b *WantedAnchorBuilder) WithSubType(subType string) *WantedAnchorBuilder { + b.wantedAnchor.subType = subType + return b +} + +// Build constructs the anchor from the builder's specification +func (b *WantedAnchorBuilder) Build() (WantedAnchor, error) { + return b.wantedAnchor, nil +} + +// MarshalJSON ... +func (a *WantedAnchor) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Name string `json:"name"` + SubType string `json:"sub_type"` + }{ + Name: a.name, + SubType: a.subType, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder_test.go new file mode 100644 index 0000000..2d9e74d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_anchor_builder_test.go @@ -0,0 +1,25 @@ +package dynamic + +import ( + "fmt" +) + +func ExampleWantedAnchorBuilder() { + aadhaarAnchor, err := (&WantedAnchorBuilder{}). + WithValue("NATIONAL_ID"). + WithSubType("AADHAAR"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + aadhaarJSON, err := aadhaarAnchor.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println("Aadhaar:", string(aadhaarJSON)) + // Output: Aadhaar: {"name":"NATIONAL_ID","sub_type":"AADHAAR"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_builder.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_builder.go new file mode 100644 index 0000000..bc2c76f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_builder.go @@ -0,0 +1,81 @@ +package dynamic + +import ( + "encoding/json" + "errors" +) + +type constraintInterface interface { + MarshalJSON() ([]byte, error) + isConstraint() bool // This function is not used but makes inheritance explicit +} + +// WantedAttributeBuilder generates the payload for specifying a single wanted +// attribute as part of a dynamic scenario +type WantedAttributeBuilder struct { + attr WantedAttribute +} + +// WantedAttribute represents a wanted attribute in a dynamic sharing policy +type WantedAttribute struct { + name string + derivation string + constraints []constraintInterface + acceptSelfAsserted bool + Optional bool +} + +// WithName sets the name of the wanted attribute +func (builder *WantedAttributeBuilder) WithName(name string) *WantedAttributeBuilder { + builder.attr.name = name + return builder +} + +// WithDerivation sets the derivation +func (builder *WantedAttributeBuilder) WithDerivation(derivation string) *WantedAttributeBuilder { + builder.attr.derivation = derivation + return builder +} + +// WithConstraint adds a constraint to a wanted attribute +func (builder *WantedAttributeBuilder) WithConstraint(constraint constraintInterface) *WantedAttributeBuilder { + builder.attr.constraints = append(builder.attr.constraints, constraint) + return builder +} + +// WithAcceptSelfAsserted allows self-asserted user details, such as those from Aadhar +func (builder *WantedAttributeBuilder) WithAcceptSelfAsserted(accept bool) *WantedAttributeBuilder { + builder.attr.acceptSelfAsserted = accept + return builder +} + +// Build generates the wanted attribute's specification +func (builder *WantedAttributeBuilder) Build() (WantedAttribute, error) { + if builder.attr.constraints == nil { + builder.attr.constraints = make([]constraintInterface, 0) + } + + var err error + if len(builder.attr.name) == 0 { + err = errors.New("Wanted attribute names must not be empty") + } + + return builder.attr, err +} + +// MarshalJSON returns the JSON encoding +func (attr *WantedAttribute) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Name string `json:"name"` + Derivation string `json:"derivation,omitempty"` + Constraints []constraintInterface `json:"constraints,omitempty"` + AcceptSelfAsserted bool `json:"accept_self_asserted"` + Optional bool `json:"optional,omitempty"` + }{ + Name: attr.name, + Derivation: attr.derivation, + Constraints: attr.constraints, + AcceptSelfAsserted: attr.acceptSelfAsserted, + Optional: attr.Optional, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_test.go new file mode 100644 index 0000000..43d189d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/dynamic/wanted_attribute_test.go @@ -0,0 +1,154 @@ +package dynamic + +import ( + "encoding/json" + "fmt" + "testing" + + "gotest.tools/v3/assert" +) + +func ExampleWantedAttributeBuilder_WithName() { + builder := (&WantedAttributeBuilder{}).WithName("TEST NAME") + attribute, err := builder.Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(attribute.name) + // Output: TEST NAME +} + +func ExampleWantedAttributeBuilder_WithDerivation() { + attribute, err := (&WantedAttributeBuilder{}). + WithDerivation("TEST DERIVATION"). + WithName("TEST NAME"). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(attribute.derivation) + // Output: TEST DERIVATION +} + +func ExampleWantedAttributeBuilder_WithConstraint() { + constraint, err := (&SourceConstraintBuilder{}). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithConstraint(&constraint). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","constraints":[{"type":"SOURCE","preferred_sources":{"anchors":[],"soft_preference":false}}],"accept_self_asserted":false} +} + +func ExampleWantedAttributeBuilder_WithAcceptSelfAsserted() { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithAcceptSelfAsserted(true). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","accept_self_asserted":true} +} + +func ExampleWantedAttributeBuilder_WithAcceptSelfAsserted_false() { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithAcceptSelfAsserted(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","accept_self_asserted":false} +} + +func ExampleWantedAttributeBuilder_optional_true() { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + WithAcceptSelfAsserted(false). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + attribute.Optional = true + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"TEST NAME","accept_self_asserted":false,"optional":true} +} + +func TestWantedAttributeBuilder_Optional_IsOmittedByDefault(t *testing.T) { + attribute, err := (&WantedAttributeBuilder{}). + WithName("TEST NAME"). + Build() + if err != nil { + t.Errorf("error: %s", err.Error()) + } + + marshalledJSON, err := attribute.MarshalJSON() + if err != nil { + t.Errorf("error: %s", err.Error()) + } + + attributeMap := unmarshalJSONIntoMap(t, marshalledJSON) + + optional := attributeMap["optional"] + + if optional != nil { + t.Errorf("expected `optional` to be nil, but was: '%v'", optional) + } +} + +func unmarshalJSONIntoMap(t *testing.T, byteValue []byte) (result map[string]interface{}) { + var unmarshalled interface{} + err := json.Unmarshal(byteValue, &unmarshalled) + assert.NilError(t, err) + + return unmarshalled.(map[string]interface{}) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension.go new file mode 100644 index 0000000..dc57fbb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension.go @@ -0,0 +1,46 @@ +package extension + +import ( + "encoding/json" +) + +// Extension is a generic type of extension that can be used where a more +// specialised Extension type is not available +type Extension struct { + extensionType string + content interface{} +} + +// Builder is used to construct an Extension object +type Builder struct { + extension Extension +} + +// WithType sets the extension type string +func (builder *Builder) WithType(extensionType string) *Builder { + builder.extension.extensionType = extensionType + return builder +} + +// WithContent attaches data to the Extension. The content must implement JSON +// serialization +func (builder *Builder) WithContent(content interface{}) *Builder { + builder.extension.content = content + return builder +} + +// Build constructs the Extension +func (builder *Builder) Build() (Extension, error) { + return builder.extension, nil +} + +// MarshalJSON returns the JSON encoding +func (extension Extension) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Content interface{} `json:"content"` + }{ + Type: extension.extensionType, + Content: extension.content, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension_test.go new file mode 100644 index 0000000..5ac07e7 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/extension_test.go @@ -0,0 +1,24 @@ +package extension + +import ( + "fmt" +) + +func ExampleExtension() { + content := "SOME CONTENT" + extType := "SOME_TYPE" + extension, err := (&Builder{}).WithContent(content).WithType(extType).Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := extension.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"SOME_TYPE","content":"SOME CONTENT"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension.go new file mode 100644 index 0000000..6120b3b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension.go @@ -0,0 +1,78 @@ +package extension + +import ( + "encoding/json" +) + +const ( + locationConstraintExtensionTypeConst = "LOCATION_CONSTRAINT" +) + +// LocationConstraintExtensionBuilder is used to construct a LocationConstraintExtension +type LocationConstraintExtensionBuilder struct { + extension LocationConstraintExtension +} + +// LocationConstraintExtension is an extension representing a geographic constraint +type LocationConstraintExtension struct { + latitude float64 + longitude float64 + radius float64 + uncertainty float64 +} + +// WithLatitude sets the latitude of the location constraint +func (builder *LocationConstraintExtensionBuilder) WithLatitude(latitude float64) *LocationConstraintExtensionBuilder { + builder.extension.latitude = latitude + return builder +} + +// WithLongitude sets the longitude of the location constraint +func (builder *LocationConstraintExtensionBuilder) WithLongitude(longitude float64) *LocationConstraintExtensionBuilder { + builder.extension.longitude = longitude + return builder +} + +// WithRadius sets the radius within which the location constraint will be satisfied +func (builder *LocationConstraintExtensionBuilder) WithRadius(radius float64) *LocationConstraintExtensionBuilder { + builder.extension.radius = radius + return builder +} + +// WithMaxUncertainty sets the max uncertainty allowed by the location constraint extension +func (builder *LocationConstraintExtensionBuilder) WithMaxUncertainty(uncertainty float64) *LocationConstraintExtensionBuilder { + builder.extension.uncertainty = uncertainty + return builder +} + +// Build constructs a LocationConstraintExtension from the builder +func (builder *LocationConstraintExtensionBuilder) Build() (LocationConstraintExtension, error) { + return builder.extension, nil +} + +// MarshalJSON returns the JSON encoding +func (extension LocationConstraintExtension) MarshalJSON() ([]byte, error) { + type location struct { + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Radius float64 `json:"radius"` + MaxUncertainty float64 `json:"max_uncertainty_radius"` + } + type content struct { + Location location `json:"expected_device_location"` + } + return json.Marshal(&struct { + Type string `json:"type"` + Content content `json:"content"` + }{ + Type: locationConstraintExtensionTypeConst, + Content: content{ + Location: location{ + Latitude: extension.latitude, + Longitude: extension.longitude, + Radius: extension.radius, + MaxUncertainty: extension.uncertainty, + }, + }, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension_test.go new file mode 100644 index 0000000..05e1621 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/location_constraint_extension_test.go @@ -0,0 +1,27 @@ +package extension + +import ( + "fmt" +) + +func ExampleLocationConstraintExtension() { + extension, err := (&LocationConstraintExtensionBuilder{}). + WithLatitude(51.511831). + WithLongitude(-0.081446). + WithRadius(0.001). + WithMaxUncertainty(0.001). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := extension.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"LOCATION_CONSTRAINT","content":{"expected_device_location":{"latitude":51.511831,"longitude":-0.081446,"radius":0.001,"max_uncertainty_radius":0.001}}} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension.go new file mode 100644 index 0000000..d169424 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension.go @@ -0,0 +1,64 @@ +package extension + +import ( + "encoding/json" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" +) + +const ( + thirdPartyAttributeExtensionTypeConst = "THIRD_PARTY_ATTRIBUTE" +) + +// ThirdPartyAttributeExtensionBuilder is used to construct a ThirdPartyAttributeExtension +type ThirdPartyAttributeExtensionBuilder struct { + extension ThirdPartyAttributeExtension +} + +// ThirdPartyAttributeExtension is an extension representing the issuance of a third party attribute +type ThirdPartyAttributeExtension struct { + expiryDate *time.Time + definitions []attribute.Definition +} + +// WithExpiryDate sets the expiry date of the extension as a UTC timestamp +func (builder *ThirdPartyAttributeExtensionBuilder) WithExpiryDate(expiryDate *time.Time) *ThirdPartyAttributeExtensionBuilder { + builder.extension.expiryDate = expiryDate + return builder +} + +// WithDefinition adds an attribute.AttributeDefinition to the list of definitions +func (builder *ThirdPartyAttributeExtensionBuilder) WithDefinition(definition attribute.Definition) *ThirdPartyAttributeExtensionBuilder { + builder.extension.definitions = append(builder.extension.definitions, definition) + return builder +} + +// WithDefinitions sets the array of attribute.AttributeDefinition on the extension +func (builder *ThirdPartyAttributeExtensionBuilder) WithDefinitions(definitions []attribute.Definition) *ThirdPartyAttributeExtensionBuilder { + builder.extension.definitions = definitions + return builder +} + +// Build creates a ThirdPartyAttributeExtension using the supplied values +func (builder *ThirdPartyAttributeExtensionBuilder) Build() (ThirdPartyAttributeExtension, error) { + return builder.extension, nil +} + +// MarshalJSON returns the JSON encoding +func (extension ThirdPartyAttributeExtension) MarshalJSON() ([]byte, error) { + type thirdPartyAttributeExtension struct { + ExpiryDate string `json:"expiry_date"` + Definitions []attribute.Definition `json:"definitions"` + } + return json.Marshal(&struct { + Type string `json:"type"` + Content thirdPartyAttributeExtension `json:"content"` + }{ + Type: thirdPartyAttributeExtensionTypeConst, + Content: thirdPartyAttributeExtension{ + ExpiryDate: extension.expiryDate.UTC().Format("2006-01-02T15:04:05.000Z"), + Definitions: extension.definitions, + }, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension_test.go new file mode 100644 index 0000000..e51f661 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/third_party_attribute_extension_test.go @@ -0,0 +1,142 @@ +package extension + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "gotest.tools/v3/assert" +) + +func createDefinitionByName(name string) attribute.Definition { + return attribute.NewAttributeDefinition(name) +} + +func ExampleThirdPartyAttributeExtension() { + attributeDefinition := attribute.NewAttributeDefinition("some_value") + + datetime, err := time.Parse("2006-01-02T15:04:05.000Z", "2019-10-30T12:10:09.458Z") + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + extension, err := (&ThirdPartyAttributeExtensionBuilder{}). + WithExpiryDate(&datetime). + WithDefinition(attributeDefinition). + Build() + + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := extension.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"THIRD_PARTY_ATTRIBUTE","content":{"expiry_date":"2019-10-30T12:10:09.458Z","definitions":[{"name":"some_value"}]}} +} + +func TestWithDefinitionShouldAddToList(t *testing.T) { + datetime, err := time.Parse("2006-01-02T15:04:05.000Z", "2019-10-30T12:10:09.458Z") + assert.NilError(t, err) + + definitionList := []attribute.Definition{ + createDefinitionByName("some_attribute"), + createDefinitionByName("some_other_attribute"), + } + + someOtherDefinition := createDefinitionByName("wanted_definition") + + extension, err := (&ThirdPartyAttributeExtensionBuilder{}). + WithExpiryDate(&datetime). + WithDefinitions(definitionList). + WithDefinition(someOtherDefinition). + Build() + + assert.NilError(t, err) + assert.Equal(t, len(extension.definitions), 3) + assert.Equal(t, extension.definitions[0].Name(), "some_attribute") + assert.Equal(t, extension.definitions[1].Name(), "some_other_attribute") + assert.Equal(t, extension.definitions[2].Name(), "wanted_definition") +} + +func TestWithDefinitionsShouldOverwriteList(t *testing.T) { + datetime, err := time.Parse("2006-01-02T15:04:05.000Z", "2019-10-30T12:10:09.458Z") + assert.NilError(t, err) + + definitionList := []attribute.Definition{ + createDefinitionByName("some_attribute"), + createDefinitionByName("some_other_attribute"), + } + + someOtherDefinition := createDefinitionByName("wanted_definition") + + extension, err := (&ThirdPartyAttributeExtensionBuilder{}). + WithExpiryDate(&datetime). + WithDefinition(someOtherDefinition). + WithDefinitions(definitionList). + Build() + + assert.NilError(t, err) + assert.Equal(t, len(extension.definitions), 2) + assert.Equal(t, extension.definitions[0].Name(), "some_attribute") + assert.Equal(t, extension.definitions[1].Name(), "some_other_attribute") +} + +var expiryDates = []struct { + in time.Time + expected string +}{ + {time.Date(2051, 01, 13, 19, 50, 53, 1, time.UTC), "2051-01-13T19:50:53.000Z"}, + {time.Date(2026, 02, 02, 22, 04, 05, 123, time.UTC), "2026-02-02T22:04:05.000Z"}, + {time.Date(2051, 03, 13, 19, 50, 53, 9999, time.UTC), "2051-03-13T19:50:53.000Z"}, + {time.Date(2051, 04, 13, 19, 50, 53, 999999, time.UTC), "2051-04-13T19:50:53.000Z"}, + {time.Date(2026, 01, 31, 22, 04, 05, 1232567, time.UTC), "2026-01-31T22:04:05.001Z"}, + {time.Date(2026, 01, 31, 22, 04, 05, 17777777, time.UTC), "2026-01-31T22:04:05.017Z"}, + {time.Date(2026, 07, 31, 22, 04, 05, 000777777, time.UTC), "2026-07-31T22:04:05.000Z"}, + {time.Date(2026, 01, 02, 22, 04, 05, 123456789, time.UTC), "2026-01-02T22:04:05.123Z"}, + {time.Date(2028, 10, 02, 10, 00, 00, 0, time.FixedZone("UTC-5", -5*60*60)), "2028-10-02T15:00:00.000Z"}, + {time.Date(2028, 10, 02, 10, 00, 00, 0, time.FixedZone("UTC+11", 11*60*60)), "2028-10-01T23:00:00.000Z"}, + {time.Unix(1734567899, 0), "2024-12-19T00:24:59.000Z"}, + {time.Unix(2234567891, 0), "2040-10-23T01:18:11.000Z"}, +} + +func TestExpiryDatesAreFormattedCorrectly(t *testing.T) { + attributeDefinition := attribute.NewAttributeDefinition("some_value") + + for _, date := range expiryDates { + extension, err := (&ThirdPartyAttributeExtensionBuilder{}). + WithExpiryDate(&date.in). + WithDefinition(attributeDefinition). + Build() + + assert.NilError(t, err) + + marshalledJson, err := extension.MarshalJSON() + assert.NilError(t, err) + + attributeIssuanceDetailsJson := unmarshalJSONIntoMap(t, marshalledJson) + + content := attributeIssuanceDetailsJson["content"].(map[string]interface{}) + result := content["expiry_date"] + + if result != date.expected { + t.Errorf("got %q, want %q", result, date.expected) + } + } +} + +func unmarshalJSONIntoMap(t *testing.T, byteValue []byte) (result map[string]interface{}) { + var unmarshalled interface{} + err := json.Unmarshal(byteValue, &unmarshalled) + assert.NilError(t, err) + + return unmarshalled.(map[string]interface{}) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension.go new file mode 100644 index 0000000..1618c5e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension.go @@ -0,0 +1,42 @@ +package extension + +import ( + "encoding/json" +) + +const ( + transactionalFlowExtensionTypeConst = "TRANSACTIONAL_FLOW" +) + +// TransactionalFlowExtension represents a type of extension in a dynamic share +type TransactionalFlowExtension struct { + content interface{} +} + +// TransactionalFlowExtensionBuilder constructs a TransactionalFlowExtension +type TransactionalFlowExtensionBuilder struct { + extension TransactionalFlowExtension +} + +// WithContent sets the payload data for a TransactionalFlowExtension. The +// content must implement JSON serialization +func (builder *TransactionalFlowExtensionBuilder) WithContent(content interface{}) *TransactionalFlowExtensionBuilder { + builder.extension.content = content + return builder +} + +// Build constructs a TransactionalFlowExtension +func (builder *TransactionalFlowExtensionBuilder) Build() (TransactionalFlowExtension, error) { + return builder.extension, nil +} + +// MarshalJSON returns the JSON encoding +func (extension TransactionalFlowExtension) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Content interface{} `json:"content"` + }{ + Type: transactionalFlowExtensionTypeConst, + Content: extension.content, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension_test.go new file mode 100644 index 0000000..cda4e36 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extension/transactional_flow_extension_test.go @@ -0,0 +1,27 @@ +package extension + +import ( + "encoding/json" + "fmt" +) + +func ExampleTransactionalFlowExtension() { + content := "SOME CONTENT" + + extension, err := (&TransactionalFlowExtensionBuilder{}). + WithContent(content). + Build() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + data, err := json.Marshal(extension) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(data)) + // Output: {"type":"TRANSACTIONAL_FLOW","content":"SOME CONTENT"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data.go new file mode 100644 index 0000000..b61ba4e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data.go @@ -0,0 +1,50 @@ +package extra + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" +) + +// Data represents extra pieces information on the receipt. +// Initialize with NewExtraData or DefaultExtraData +type Data struct { + attributeIssuanceDetails *attribute.IssuanceDetails +} + +// DefaultExtraData initialises the ExtraData struct +func DefaultExtraData() (extraData *Data) { + return &Data{ + attributeIssuanceDetails: nil, + } +} + +// NewExtraData takes a base64 encoded string and parses it into ExtraData +func NewExtraData(extraDataBytes []byte) (*Data, error) { + var err error + var extraData = DefaultExtraData() + + extraDataProto := &yotiprotoshare.ExtraData{} + if err = proto.Unmarshal(extraDataBytes, extraDataProto); err != nil { + return extraData, err + } + + var attributeIssuanceDetails *attribute.IssuanceDetails + + for _, de := range extraDataProto.GetList() { + if de.Type == yotiprotoshare.DataEntry_THIRD_PARTY_ATTRIBUTE { + attributeIssuanceDetails, err = attribute.ParseIssuanceDetails(de.Value) + + return &Data{ + attributeIssuanceDetails: attributeIssuanceDetails, + }, err + } + } + + return extraData, nil +} + +// AttributeIssuanceDetails represents the details of attribute(s) to be issued by a third party. Will be nil if not provided by Yoti. +func (e Data) AttributeIssuanceDetails() (issuanceDetails *attribute.IssuanceDetails) { + return e.attributeIssuanceDetails +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data_test.go new file mode 100644 index 0000000..816dfaa --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/extra/extra_data_test.go @@ -0,0 +1,153 @@ +package extra + +import ( + "encoding/base64" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestAttributeIssuanceDetailsShouldReturnNilWhenNoDataEntries(t *testing.T) { + extraData := DefaultExtraData() + + issuanceDetails := extraData.AttributeIssuanceDetails() + + assert.Assert(t, is.Nil(issuanceDetails)) +} + +func TestShouldReturnFirstMatchingThirdPartyAttribute(t *testing.T) { + dataEntries := make([]*yotiprotoshare.DataEntry, 0) + + expiryDate := time.Now().UTC().AddDate(0, 0, 1) + var tokenValue1 = "tokenValue1" + + thirdPartyAttributeDataEntry1 := test.CreateThirdPartyAttributeDataEntry(t, &expiryDate, []string{"attributeName1"}, tokenValue1) + thirdPartyAttributeDataEntry2 := test.CreateThirdPartyAttributeDataEntry(t, &expiryDate, []string{"attributeName2"}, "tokenValue2") + + dataEntries = append(dataEntries, &thirdPartyAttributeDataEntry1, &thirdPartyAttributeDataEntry2) + protoExtraData := &yotiprotoshare.ExtraData{ + List: dataEntries, + } + + parsedExtraData, err := parseProtoExtraData(t, protoExtraData) + assert.NilError(t, err) + + result := parsedExtraData.AttributeIssuanceDetails() + + var tokenBytes = []byte(tokenValue1) + var base64EncodedToken = base64.StdEncoding.EncodeToString(tokenBytes) + + assert.Equal(t, result.Token(), base64EncodedToken) + assert.Equal(t, result.Attributes()[0].Name(), "attributeName1") + assert.Equal(t, + result.ExpiryDate().Format("2006-01-02T15:04:05.000Z"), + expiryDate.Format("2006-01-02T15:04:05.000Z")) +} + +func TestShouldParseMultipleIssuingAttributes(t *testing.T) { + var base64ExtraData = test.GetTestFileAsString(t, "../test/fixtures/test_extra_data.txt") + rawExtraData, err := base64.StdEncoding.DecodeString(base64ExtraData) + assert.NilError(t, err) + + extraData, err := NewExtraData(rawExtraData) + assert.NilError(t, err) + + result := extraData.AttributeIssuanceDetails() + + assert.Equal(t, result.Token(), "c29tZUlzc3VhbmNlVG9rZW4=") + assert.Equal(t, + result.ExpiryDate().Format("2006-01-02T15:04:05.000Z"), + time.Date(2019, time.October, 15, 22, 04, 05, 123000000, time.UTC).Format("2006-01-02T15:04:05.000Z")) + assert.Equal(t, result.Attributes()[0].Name(), "com.thirdparty.id") + assert.Equal(t, result.Attributes()[1].Name(), "com.thirdparty.other_id") +} + +func TestShouldHandleNoExpiryDate(t *testing.T) { + var protoDefinitions []*yotiprotoshare.Definition + + protoDefinitions = append(protoDefinitions, &yotiprotoshare.Definition{Name: "attribute.name"}) + + thirdPartyAttribute := &yotiprotoshare.ThirdPartyAttribute{ + IssuanceToken: []byte("tokenValue"), + IssuingAttributes: &yotiprotoshare.IssuingAttributes{ + ExpiryDate: "", + Definitions: protoDefinitions, + }, + } + + marshalledThirdPartyAttribute, err := proto.Marshal(thirdPartyAttribute) + assert.NilError(t, err) + + result, err := processThirdPartyAttribute(t, marshalledThirdPartyAttribute) + assert.NilError(t, err) + + assert.Assert(t, is.Nil(result.ExpiryDate())) +} + +func TestShouldHandleNoIssuingAttributes(t *testing.T) { + var tokenValueBytes = []byte("token") + thirdPartyAttribute := &yotiprotoshare.ThirdPartyAttribute{ + IssuanceToken: tokenValueBytes, + IssuingAttributes: &yotiprotoshare.IssuingAttributes{}, + } + + marshalledThirdPartyAttribute, err := proto.Marshal(thirdPartyAttribute) + assert.NilError(t, err) + + result, err := processThirdPartyAttribute(t, marshalledThirdPartyAttribute) + + assert.NilError(t, err) + assert.Equal(t, base64.StdEncoding.EncodeToString(tokenValueBytes), result.Token()) +} + +func TestShouldHandleNoIssuingAttributeDefinitions(t *testing.T) { + var tokenValueBytes = []byte("token") + + thirdPartyAttribute := &yotiprotoshare.ThirdPartyAttribute{ + IssuanceToken: tokenValueBytes, + IssuingAttributes: &yotiprotoshare.IssuingAttributes{ + ExpiryDate: time.Now().UTC().AddDate(0, 0, 1).Format("2006-01-02T15:04:05.000Z"), + Definitions: []*yotiprotoshare.Definition{}, + }, + } + + marshalledThirdPartyAttribute, err := proto.Marshal(thirdPartyAttribute) + assert.NilError(t, err) + + result, err := processThirdPartyAttribute(t, marshalledThirdPartyAttribute) + + assert.NilError(t, err) + assert.Equal(t, base64.StdEncoding.EncodeToString(tokenValueBytes), result.Token()) +} + +func processThirdPartyAttribute(t *testing.T, marshalledThirdPartyAttribute []byte) (*attribute.IssuanceDetails, error) { + dataEntries := make([]*yotiprotoshare.DataEntry, 0) + + thirdPartyAttributeDataEntry := yotiprotoshare.DataEntry{ + Type: yotiprotoshare.DataEntry_THIRD_PARTY_ATTRIBUTE, + Value: marshalledThirdPartyAttribute, + } + + dataEntries = append(dataEntries, &thirdPartyAttributeDataEntry) + protoExtraData := &yotiprotoshare.ExtraData{ + List: dataEntries, + } + + parsedExtraData, err := parseProtoExtraData(t, protoExtraData) + + return parsedExtraData.AttributeIssuanceDetails(), err +} + +func parseProtoExtraData(t *testing.T, protoExtraData *yotiprotoshare.ExtraData) (*Data, error) { + extraDataMarshalled, err := proto.Marshal(protoExtraData) + assert.NilError(t, err) + + return NewExtraData(extraDataMarshalled) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file.go new file mode 100644 index 0000000..a002b96 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file.go @@ -0,0 +1,21 @@ +package file + +import ( + "io" + "os" +) + +// ReadFile reads a file until an error or EOF +func ReadFile(filename string) ([]byte, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + + buffer, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + return buffer, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file_test.go new file mode 100644 index 0000000..cb68adb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/file/file_test.go @@ -0,0 +1,18 @@ +package file + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestFile_ReadFile(t *testing.T) { + _, err := ReadFile("../test/test-key.pem") + assert.NilError(t, err) +} + +func TestFile_ReadFile_ShouldFailForFileNotFound(t *testing.T) { + MissingFileName := "/tmp/file_not_found" + _, err := ReadFile(MissingFileName) + assert.Check(t, err != nil) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.mod b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.mod new file mode 100644 index 0000000..c1c87ab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.mod @@ -0,0 +1,10 @@ +module github.com/getyoti/yoti-go-sdk/v3 + +require ( + google.golang.org/protobuf v1.28.0 + gotest.tools/v3 v3.3.0 +) + +require github.com/google/go-cmp v0.5.5 // indirect + +go 1.19 diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.sum b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.sum new file mode 100644 index 0000000..7b99939 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/go.sum @@ -0,0 +1,33 @@ +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image.go new file mode 100644 index 0000000..35796b5 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image.go @@ -0,0 +1,45 @@ +package media + +const ( + // ImageTypeJPEG JPEG format + ImageTypeJPEG string = "image/jpeg" + + // ImageTypePNG PNG format + ImageTypePNG string = "image/png" +) + +// PNGImage holds the binary data of a PNG image. +type PNGImage []byte + +// Base64URL is PNG image encoded as a base64 URL. +func (i PNGImage) Base64URL() string { + return base64URL(i.MIME(), i) +} + +// MIME returns the MIME type for PNG images. +func (PNGImage) MIME() string { + return ImageTypePNG +} + +// Data returns the PNG image as raw bytes. +func (i PNGImage) Data() []byte { + return i +} + +// JPEGImage holds the binary data of a JPEG image. +type JPEGImage []byte + +// Base64URL is JPEG image encoded as a base64 URL. +func (i JPEGImage) Base64URL() string { + return base64URL(i.MIME(), i) +} + +// MIME returns the MIME type for JPEG images. +func (JPEGImage) MIME() string { + return ImageTypeJPEG +} + +// Data returns the JPEG image as raw bytes. +func (i JPEGImage) Data() []byte { + return i +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image_test.go new file mode 100644 index 0000000..85f162c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/image_test.go @@ -0,0 +1,31 @@ +package media + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +const ( + imageBase64Value = "dmFsdWU=" +) + +func TestImage_Base64URL_CreateJpegImage(t *testing.T) { + imageBytes := []byte("value") + + result := JPEGImage(imageBytes) + expectedDataURL := "data:image/jpeg;base64," + imageBase64Value + + assert.Equal(t, expectedDataURL, result.Base64URL()) + assert.DeepEqual(t, imageBytes, result.Data()) +} + +func TestImage_Base64URL_CreatePngImage(t *testing.T) { + imageBytes := []byte("value") + + result := PNGImage(imageBytes) + expectedDataURL := "data:image/png;base64," + imageBase64Value + + assert.Equal(t, expectedDataURL, result.Base64URL()) + assert.DeepEqual(t, imageBytes, result.Data()) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue.go new file mode 100644 index 0000000..4b597b1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue.go @@ -0,0 +1,65 @@ +package media + +import ( + "encoding/base64" + "fmt" +) + +// Media holds a piece of binary data. +type Media interface { + // Base64URL is the media encoded as a base64 URL. + Base64URL() string + + // MIME returns the media's MIME type. + MIME() string + + // Data returns the media's raw data. + Data() []byte +} + +// NewMedia will create a new appropriate media structure based on the MIME +// type provided. If no suitable structure exists, a Generic one will be used. +func NewMedia(mime string, data []byte) Media { + switch mime { + case ImageTypeJPEG: + return JPEGImage(data) + case ImageTypePNG: + return PNGImage(data) + default: + return NewGeneric(mime, data) + } +} + +// Generic holds binary data defined by its MIME type. +type Generic struct { + mime string + data []byte +} + +// NewGeneric creates a new Generic object. +func NewGeneric(mime string, data []byte) Generic { + return Generic{ + mime: mime, + data: data, + } +} + +// MIME returns the media's MIME type. +func (g Generic) MIME() string { + return g.mime +} + +// Base64URL is the media encoded as a base64 URL. +func (g Generic) Base64URL() string { + return base64URL(g.MIME(), g.data) +} + +// Data returns the media's raw data. +func (g Generic) Data() []byte { + return g.data +} + +func base64URL(mimeType string, data []byte) string { + base64EncodedImage := base64.StdEncoding.EncodeToString(data) + return fmt.Sprintf("data:%s;base64,%s", mimeType, base64EncodedImage) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue_test.go new file mode 100644 index 0000000..609907a --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/media/mediavalue_test.go @@ -0,0 +1,35 @@ +package media + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestGeneric_Base64URL_create(t *testing.T) { + dataBytes := []byte("value") + mime := "foo/bar" + + result := NewGeneric(mime, dataBytes) + + expectedDataURL := "data:" + mime + ";base64," + imageBase64Value + + assert.Equal(t, expectedDataURL, result.Base64URL()) + assert.DeepEqual(t, dataBytes, result.Data()) +} + +func TestNewMedia(t *testing.T) { + dataBytes := []byte("value") + + v := NewMedia(ImageTypeJPEG, dataBytes) + _, ok := v.(JPEGImage) + assert.Assert(t, ok) + + v = NewMedia(ImageTypePNG, dataBytes) + _, ok = v.(PNGImage) + assert.Assert(t, ok) + + v = NewMedia("foo/bar", dataBytes) + _, ok = v.(Generic) + assert.Assert(t, ok) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/activity_details.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/activity_details.go new file mode 100644 index 0000000..71b3ebf --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/activity_details.go @@ -0,0 +1,48 @@ +package profile + +import ( + "time" + + "github.com/getyoti/yoti-go-sdk/v3/extra" +) + +// ActivityDetails represents the result of an activity between a user and the application. +type ActivityDetails struct { + UserProfile UserProfile + rememberMeID string + parentRememberMeID string + timestamp time.Time + receiptID string + ApplicationProfile ApplicationProfile + extraData *extra.Data +} + +// RememberMeID is a unique, stable identifier for a user in the context +// of an application. You can use it to identify returning users. +// This value will be different for the same user in different applications. +func (a ActivityDetails) RememberMeID() string { + return a.rememberMeID +} + +// ParentRememberMeID is a unique, stable identifier for a user in the +// context of an organisation. You can use it to identify returning users. +// This value is consistent for a given user across different applications +// belonging to a single organisation. +func (a ActivityDetails) ParentRememberMeID() string { + return a.parentRememberMeID +} + +// Timestamp is the Time and date of the sharing activity +func (a ActivityDetails) Timestamp() time.Time { + return a.timestamp +} + +// ReceiptID identifies a completed activity +func (a ActivityDetails) ReceiptID() string { + return a.receiptID +} + +// ExtraData represents extra pieces information on the receipt +func (a ActivityDetails) ExtraData() *extra.Data { + return a.extraData +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/address.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/address.go new file mode 100644 index 0000000..9d66fab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/address.go @@ -0,0 +1,52 @@ +package profile + +import ( + "reflect" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +func getFormattedAddress(profile *UserProfile, formattedAddress string) *yotiprotoattr.Attribute { + proto := getProtobufAttribute(*profile, consts.AttrStructuredPostalAddress) + + return &yotiprotoattr.Attribute{ + Name: consts.AttrAddress, + Value: []byte(formattedAddress), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: proto.Anchors, + } +} + +func ensureAddressProfile(p *UserProfile) *attribute.StringAttribute { + if structuredPostalAddress, err := p.StructuredPostalAddress(); err == nil { + if (structuredPostalAddress != nil && !reflect.DeepEqual(structuredPostalAddress, attribute.JSONAttribute{})) { + var formattedAddress string + formattedAddress, err = retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress.Value()) + if err == nil && formattedAddress != "" { + return attribute.NewString(getFormattedAddress(p, formattedAddress)) + } + } + } + + return nil +} + +func retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress interface{}) (address string, err error) { + parsedStructuredAddressMap := structuredPostalAddress.(map[string]interface{}) + if formattedAddress, ok := parsedStructuredAddressMap["formatted_address"]; ok { + return formattedAddress.(string), nil + } + return +} + +func getProtobufAttribute(profile UserProfile, key string) *yotiprotoattr.Attribute { + for _, v := range profile.attributeSlice { + if v.Name == key { + return v + } + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/application_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/application_profile.go new file mode 100644 index 0000000..473a0ad --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/application_profile.go @@ -0,0 +1,50 @@ +package profile + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// Attribute names for application attributes +const ( + AttrConstApplicationName = "application_name" + AttrConstApplicationURL = "application_url" + AttrConstApplicationLogo = "application_logo" + AttrConstApplicationReceiptBGColor = "application_receipt_bgcolor" +) + +// ApplicationProfile is the profile of an application with convenience methods +// to access well-known attributes. +type ApplicationProfile struct { + baseProfile +} + +func newApplicationProfile(attributes *yotiprotoattr.AttributeList) ApplicationProfile { + return ApplicationProfile{ + baseProfile{ + attributeSlice: createAttributeSlice(attributes), + }, + } +} + +// ApplicationName is the name of the application +func (p ApplicationProfile) ApplicationName() *attribute.StringAttribute { + return p.GetStringAttribute(AttrConstApplicationName) +} + +// ApplicationURL is the URL where the application is available at +func (p ApplicationProfile) ApplicationURL() *attribute.StringAttribute { + return p.GetStringAttribute(AttrConstApplicationURL) +} + +// ApplicationReceiptBgColor is the background colour that will be displayed on +// each receipt the user gets as a result of a share with the application. +func (p ApplicationProfile) ApplicationReceiptBgColor() *attribute.StringAttribute { + return p.GetStringAttribute(AttrConstApplicationReceiptBGColor) +} + +// ApplicationLogo is the logo of the application that will be displayed to +// those users that perform a share with it. +func (p ApplicationProfile) ApplicationLogo() *attribute.ImageAttribute { + return p.GetImageAttribute(AttrConstApplicationLogo) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications.go new file mode 100644 index 0000000..a7655d0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications.go @@ -0,0 +1,34 @@ +package attribute + +import ( + "strconv" + "strings" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// AgeVerification encapsulates the result of a single age verification +// as part of a share +type AgeVerification struct { + Age int + CheckType string + Result bool + Attribute *yotiprotoattr.Attribute +} + +// NewAgeVerification constructs an AgeVerification from a protobuffer +func NewAgeVerification(attr *yotiprotoattr.Attribute) (verification AgeVerification, err error) { + split := strings.Split(attr.Name, ":") + verification.Age, err = strconv.Atoi(split[1]) + verification.CheckType = split[0] + + if string(attr.Value) == "true" { + verification.Result = true + } else { + verification.Result = false + } + + verification.Attribute = attr + + return +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications_test.go new file mode 100644 index 0000000..b3a6e08 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/age_verifications_test.go @@ -0,0 +1,42 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestNewAgeVerification_ValueTrue(t *testing.T) { + attribute := &yotiprotoattr.Attribute{ + Name: "age_over:18", + Value: []byte("true"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + ageVerification, err := NewAgeVerification(attribute) + + assert.NilError(t, err) + + assert.Equal(t, ageVerification.Age, 18) + assert.Equal(t, ageVerification.CheckType, "age_over") + assert.Equal(t, ageVerification.Result, true) +} + +func TestNewAgeVerification_ValueFalse(t *testing.T) { + attribute := &yotiprotoattr.Attribute{ + Name: "age_under:30", + Value: []byte("false"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + ageVerification, err := NewAgeVerification(attribute) + + assert.NilError(t, err) + + assert.Equal(t, ageVerification.Age, 30) + assert.Equal(t, ageVerification.CheckType, "age_under") + assert.Equal(t, ageVerification.Result, false) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser.go new file mode 100644 index 0000000..d1476c4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser.go @@ -0,0 +1,110 @@ +package anchor + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" + "google.golang.org/protobuf/proto" +) + +type anchorExtension struct { + Extension string `asn1:"tag:0,utf8"` +} + +var ( + sourceOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 47127, 1, 1, 1} + verifierOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 47127, 1, 1, 2} +) + +// ParseAnchors takes a slice of protobuf anchors, parses them, and returns a slice of Yoti SDK Anchors +func ParseAnchors(protoAnchors []*yotiprotoattr.Anchor) []*Anchor { + var processedAnchors []*Anchor + for _, protoAnchor := range protoAnchors { + parsedCerts := parseCertificates(protoAnchor.OriginServerCerts) + + anchorType, extension := getAnchorValuesFromCertificate(parsedCerts) + + parsedSignedTimestamp, err := parseSignedTimestamp(protoAnchor.SignedTimeStamp) + if err != nil { + continue + } + + processedAnchor := newAnchor(anchorType, parsedCerts, parsedSignedTimestamp, protoAnchor.SubType, extension) + + processedAnchors = append(processedAnchors, processedAnchor) + } + + return processedAnchors +} + +func getAnchorValuesFromCertificate(parsedCerts []*x509.Certificate) (anchorType Type, extension string) { + defaultAnchorType := TypeUnknown + + for _, cert := range parsedCerts { + for _, ext := range cert.Extensions { + var ( + value string + err error + ) + parsedAnchorType, value, err := parseExtension(ext) + if err != nil { + continue + } else if parsedAnchorType == TypeUnknown { + continue + } + return parsedAnchorType, value + } + } + + return defaultAnchorType, "" +} + +func parseExtension(ext pkix.Extension) (anchorType Type, val string, err error) { + anchorType = TypeUnknown + + switch { + case ext.Id.Equal(sourceOID): + anchorType = TypeSource + case ext.Id.Equal(verifierOID): + anchorType = TypeVerifier + default: + return anchorType, "", nil + } + + var ae anchorExtension + _, err = asn1.Unmarshal(ext.Value, &ae) + switch { + case err != nil: + return anchorType, "", fmt.Errorf("unable to unmarshal extension: %v", err) + case len(ae.Extension) == 0: + return anchorType, "", errors.New("empty extension") + default: + val = ae.Extension + } + + return anchorType, val, nil +} + +func parseSignedTimestamp(rawBytes []byte) (*yotiprotocom.SignedTimestamp, error) { + signedTimestamp := &yotiprotocom.SignedTimestamp{} + if err := proto.Unmarshal(rawBytes, signedTimestamp); err != nil { + return signedTimestamp, err + } + + return signedTimestamp, nil +} + +func parseCertificates(rawCerts [][]byte) (result []*x509.Certificate) { + for _, cert := range rawCerts { + parsedCertificate, _ := x509.ParseCertificate(cert) + + result = append(result, parsedCertificate) + } + + return result +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser_test.go new file mode 100644 index 0000000..13849a3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchor_parser_test.go @@ -0,0 +1,147 @@ +package anchor + +import ( + "crypto/x509/pkix" + "math/big" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" +) + +func assertServerCertSerialNo(t *testing.T, expectedSerialNo string, actualSerialNo *big.Int) { + expectedSerialNoBigInt := new(big.Int) + expectedSerialNoBigInt, ok := expectedSerialNoBigInt.SetString(expectedSerialNo, 10) + assert.Assert(t, ok, "Unexpected error when setting string as big int") + + assert.Equal(t, expectedSerialNoBigInt.Cmp(actualSerialNo), 0) // 0 == equivalent +} + +func createAnchorSliceFromTestFile(t *testing.T, filename string) []*yotiprotoattr.Anchor { + anchorBytes := test.DecodeTestFile(t, filename) + + protoAnchor := &yotiprotoattr.Anchor{} + err2 := proto.Unmarshal(anchorBytes, protoAnchor) + assert.NilError(t, err2) + + protoAnchors := append([]*yotiprotoattr.Anchor{}, protoAnchor) + + return protoAnchors +} + +func TestAnchorParser_parseExtension_ShouldErrorForInvalidExtension(t *testing.T) { + invalidExt := pkix.Extension{ + Id: sourceOID, + } + + _, _, err := parseExtension(invalidExt) + + assert.Check(t, err != nil) + assert.Error(t, err, "unable to unmarshal extension: asn1: syntax error: sequence truncated") +} + +func TestAnchorParser_Passport(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_passport.txt") + + parsedAnchors := ParseAnchors(anchorSlice) + + actualAnchor := parsedAnchors[0] + + assert.Equal(t, actualAnchor.Type(), TypeSource) + + expectedDate := time.Date(2018, time.April, 12, 13, 14, 32, 835537e3, time.UTC) + actualDate := actualAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "OCR" + assert.Equal(t, actualAnchor.SubType(), expectedSubType) + + expectedValue := "PASSPORT" + assert.Equal(t, actualAnchor.Value(), expectedValue) + + actualSerialNo := actualAnchor.OriginServerCerts()[0].SerialNumber + assertServerCertSerialNo(t, "277870515583559162487099305254898397834", actualSerialNo) +} + +func TestAnchorParser_DrivingLicense(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_driving_license.txt") + + parsedAnchors := ParseAnchors(anchorSlice) + resultAnchor := parsedAnchors[0] + + assert.Equal(t, resultAnchor.Type(), TypeSource) + + expectedDate := time.Date(2018, time.April, 11, 12, 13, 3, 923537e3, time.UTC) + actualDate := resultAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "" + assert.Equal(t, resultAnchor.SubType(), expectedSubType) + + expectedValue := "DRIVING_LICENCE" + assert.Equal(t, resultAnchor.Value(), expectedValue) + + actualSerialNo := resultAnchor.OriginServerCerts()[0].SerialNumber + assertServerCertSerialNo(t, "46131813624213904216516051554755262812", actualSerialNo) +} + +func TestAnchorParser_UnknownAnchor(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_unknown.txt") + + resultAnchor := ParseAnchors(anchorSlice)[0] + + expectedDate := time.Date(2019, time.March, 5, 10, 45, 11, 840037e3, time.UTC) + actualDate := resultAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "TEST UNKNOWN SUB TYPE" + expectedType := TypeUnknown + assert.Equal(t, resultAnchor.SubType(), expectedSubType) + assert.Equal(t, resultAnchor.Type(), expectedType) + assert.Equal(t, resultAnchor.Value(), "") +} + +func TestAnchorParser_YotiAdmin(t *testing.T) { + anchorSlice := createAnchorSliceFromTestFile(t, "../../../test/fixtures/test_anchor_yoti_admin.txt") + + resultAnchor := ParseAnchors(anchorSlice)[0] + + assert.Equal(t, resultAnchor.Type(), TypeVerifier) + + expectedDate := time.Date(2018, time.April, 11, 12, 13, 4, 95238e3, time.UTC) + actualDate := resultAnchor.SignedTimestamp().Timestamp().UTC() + assert.Equal(t, actualDate, expectedDate) + + expectedSubType := "" + assert.Equal(t, resultAnchor.SubType(), expectedSubType) + + expectedValue := "YOTI_ADMIN" + assert.Equal(t, resultAnchor.Value(), expectedValue) + + actualSerialNo := resultAnchor.OriginServerCerts()[0].SerialNumber + assertServerCertSerialNo(t, "256616937783084706710155170893983549581", actualSerialNo) +} + +func TestAnchors_None(t *testing.T) { + var anchorSlice []*Anchor + + sources := GetSources(anchorSlice) + assert.Equal(t, len(sources), 0, "GetSources should not return anything with empty anchors") + + verifiers := GetVerifiers(anchorSlice) + assert.Equal(t, len(verifiers), 0, "GetVerifiers should not return anything with empty anchors") +} + +func TestAnchorParser_InvalidSignedTimestamp(t *testing.T) { + var protoAnchors []*yotiprotoattr.Anchor + protoAnchors = append(protoAnchors, &yotiprotoattr.Anchor{ + SignedTimeStamp: []byte("invalidProto"), + }) + parsedAnchors := ParseAnchors(protoAnchors) + + var expectedAnchors []*Anchor + assert.DeepEqual(t, expectedAnchors, parsedAnchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors.go new file mode 100644 index 0000000..839a6e1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors.go @@ -0,0 +1,105 @@ +package anchor + +import ( + "crypto/x509" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" +) + +// Anchor is the metadata associated with an attribute. It describes how an attribute has been provided +// to Yoti (SOURCE Anchor) and how it has been verified (VERIFIER Anchor). +// If an attribute has only one SOURCE Anchor with the value set to +// "USER_PROVIDED" and zero VERIFIER Anchors, then the attribute +// is a self-certified one. +type Anchor struct { + anchorType Type + originServerCerts []*x509.Certificate + signedTimestamp SignedTimestamp + subtype string + value string +} + +func newAnchor(anchorType Type, originServerCerts []*x509.Certificate, signedTimestamp *yotiprotocom.SignedTimestamp, subtype string, value string) *Anchor { + return &Anchor{ + anchorType: anchorType, + originServerCerts: originServerCerts, + signedTimestamp: convertSignedTimestamp(signedTimestamp), + subtype: subtype, + value: value, + } +} + +// Type Anchor type, based on the Object Identifier (OID) +type Type int + +const ( + // TypeUnknown - default value + TypeUnknown Type = 1 + iota + // TypeSource - how the anchor has been sourced + TypeSource + // TypeVerifier - how the anchor has been verified + TypeVerifier +) + +// Type of the Anchor - most likely either SOURCE or VERIFIER, but it's +// possible that new Anchor types will be added in future. +func (a Anchor) Type() Type { + return a.anchorType +} + +// OriginServerCerts are the X.509 certificate chain(DER-encoded ASN.1) +// from the service that assigned the attribute. +// +// The first certificate in the chain holds the public key that can be +// used to verify the Signature field; any following entries (zero or +// more) are for intermediate certificate authorities (in order). +// +// The last certificate in the chain must be verified against the Yoti root +// CA certificate. An extension in the first certificate holds the main artifact type, +// e.g. “PASSPORT”, which can be retrieved with .Value(). +func (a Anchor) OriginServerCerts() []*x509.Certificate { + return a.originServerCerts +} + +// SignedTimestamp is the time at which the signature was created. The +// 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 *time.Time can be called with +// .Timestamp() on the result of this function. +func (a Anchor) SignedTimestamp() SignedTimestamp { + return a.signedTimestamp +} + +// SubType is an indicator of any specific processing method, or +// subcategory, pertaining to an artifact. For example, for a passport, this would be +// either "NFC" or "OCR". +func (a Anchor) SubType() string { + return a.subtype +} + +// Value identifies the provider that either sourced or verified the attribute value. +// The range of possible values is not limited. For a SOURCE anchor, expect a value like +// PASSPORT, DRIVING_LICENSE. For a VERIFIER anchor, expect a value like YOTI_ADMIN. +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, TypeSource) +} + +// 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, TypeVerifier) +} + +func filterAnchors(anchors []*Anchor, anchorType Type) (result []*Anchor) { + for _, v := range anchors { + if v.anchorType == anchorType { + result = append(result, v) + } + } + return result +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors_test.go new file mode 100644 index 0000000..ed5287e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/anchors_test.go @@ -0,0 +1,20 @@ +package anchor + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestFilterAnchors_FilterSources(t *testing.T) { + anchorSlice := []*Anchor{ + {subtype: "a", anchorType: TypeSource}, + {subtype: "b", anchorType: TypeVerifier}, + {subtype: "c", anchorType: TypeSource}, + } + sources := filterAnchors(anchorSlice, TypeSource) + assert.Equal(t, len(sources), 2) + assert.Equal(t, sources[0].subtype, "a") + assert.Equal(t, sources[1].subtype, "c") + +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/signed_timestamp.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/signed_timestamp.go new file mode 100644 index 0000000..2081b7d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/anchor/signed_timestamp.go @@ -0,0 +1,35 @@ +package anchor + +import ( + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" +) + +// SignedTimestamp is the object which contains a timestamp +type SignedTimestamp struct { + version int32 + timestamp *time.Time +} + +func convertSignedTimestamp(protoSignedTimestamp *yotiprotocom.SignedTimestamp) SignedTimestamp { + uintTimestamp := protoSignedTimestamp.Timestamp + intTimestamp := int64(uintTimestamp) + unixTime := time.Unix(intTimestamp/1e6, (intTimestamp%1e6)*1e3) + + return SignedTimestamp{ + version: protoSignedTimestamp.Version, + timestamp: &unixTime, + } +} + +// Version indicates both the version of the protobuf message in use, +// as well as the specific hash algorithms. +func (s SignedTimestamp) Version() int32 { + return s.version +} + +// Timestamp is a point in time, to the nearest microsecond. +func (s SignedTimestamp) Timestamp() *time.Time { + return s.timestamp +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_details.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_details.go new file mode 100644 index 0000000..a380150 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_details.go @@ -0,0 +1,48 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" +) + +// attributeDetails is embedded in each attribute for fields common to all +// attributes +type attributeDetails struct { + name string + contentType string + anchors []*anchor.Anchor + id *string +} + +// Name gets the attribute name +func (a attributeDetails) Name() string { + return a.name +} + +// ID gets the attribute ID +func (a attributeDetails) ID() *string { + return a.id +} + +// ContentType gets the attribute's content type description +func (a attributeDetails) ContentType() string { + return a.contentType +} + +// 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 attributeDetails) Anchors() []*anchor.Anchor { + return a.anchors +} + +// Sources returns the anchors which identify how and when an attribute value +// was acquired. +func (a attributeDetails) Sources() []*anchor.Anchor { + return anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value +// was verified by another provider. +func (a attributeDetails) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_test.go new file mode 100644 index 0000000..67b6c2b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/attribute_test.go @@ -0,0 +1,36 @@ +package attribute + +import ( + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func TestNewThirdPartyAttribute(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_third_party.txt") + + stringAttribute := NewString(protoAttribute) + + assert.Equal(t, stringAttribute.Value(), "test-third-party-attribute-0") + assert.Equal(t, stringAttribute.Name(), "com.thirdparty.id") + + assert.Equal(t, stringAttribute.Sources()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Sources()[0].SubType(), "orgName") + + assert.Equal(t, stringAttribute.Verifiers()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Verifiers()[0].SubType(), "orgName") +} + +func TestAttribute_DateOfBirth(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_date_of_birth.txt") + + dateOfBirthAttribute, err := NewDate(protoAttribute) + + assert.NilError(t, err) + + expectedDateOfBirth := time.Date(1970, time.December, 01, 0, 0, 0, 0, time.UTC) + actualDateOfBirth := dateOfBirthAttribute.Value() + + assert.Assert(t, actualDateOfBirth.Equal(expectedDateOfBirth)) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute.go new file mode 100644 index 0000000..cdc55ce --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute.go @@ -0,0 +1,39 @@ +package attribute + +import ( + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// DateAttribute is a Yoti attribute which returns a date as *time.Time for its value +type DateAttribute struct { + attributeDetails + value *time.Time +} + +// NewDate creates a new Date attribute +func NewDate(a *yotiprotoattr.Attribute) (*DateAttribute, error) { + parsedTime, err := time.Parse("2006-01-02", string(a.Value)) + if err != nil { + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &DateAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: &parsedTime, + }, nil +} + +// Value returns the value of the TimeAttribute as *time.Time +func (a *DateAttribute) Value() *time.Time { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute_test.go new file mode 100644 index 0000000..24807c9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/date_attribute_test.go @@ -0,0 +1,44 @@ +package attribute + +import ( + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestTimeAttribute_NewDate_DateOnly(t *testing.T) { + proto := yotiprotoattr.Attribute{ + Value: []byte("2011-12-25"), + } + + timeAttribute, err := NewDate(&proto) + assert.NilError(t, err) + + assert.Equal(t, *timeAttribute.Value(), time.Date(2011, 12, 25, 0, 0, 0, 0, time.UTC)) +} + +func TestTimeAttribute_DateOfBirth(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_date_of_birth.txt") + + dateOfBirthAttribute, err := NewDate(protoAttribute) + + assert.NilError(t, err) + + expectedDateOfBirth := time.Date(1970, time.December, 01, 0, 0, 0, 0, time.UTC) + actualDateOfBirth := dateOfBirthAttribute.Value() + + assert.Assert(t, actualDateOfBirth.Equal(expectedDateOfBirth)) +} + +func TestNewTime_ShouldReturnErrorForInvalidDate(t *testing.T) { + proto := yotiprotoattr.Attribute{ + Name: "example", + Value: []byte("2006-60-20"), + ContentType: yotiprotoattr.ContentType_DATE, + } + attribute, err := NewDate(&proto) + assert.Check(t, attribute == nil) + assert.ErrorContains(t, err, "month out of range") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition.go new file mode 100644 index 0000000..b0d4b8a --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition.go @@ -0,0 +1,31 @@ +package attribute + +import ( + "encoding/json" +) + +// Definition contains information about the attribute(s) issued by a third party. +type Definition struct { + name string +} + +// Name of the attribute to be issued. +func (a Definition) Name() string { + return a.name +} + +// MarshalJSON returns encoded json +func (a Definition) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Name string `json:"name"` + }{ + Name: a.name, + }) +} + +// NewAttributeDefinition returns a new AttributeDefinition +func NewAttributeDefinition(s string) Definition { + return Definition{ + name: s, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition_test.go new file mode 100644 index 0000000..b209e02 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/definition_test.go @@ -0,0 +1,18 @@ +package attribute + +import ( + "encoding/json" + "fmt" +) + +func ExampleDefinition_MarshalJSON() { + exampleDefinition := NewAttributeDefinition("exampleDefinition") + marshalledJSON, err := json.Marshal(exampleDefinition) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) + // Output: {"name":"exampleDefinition"} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute.go new file mode 100644 index 0000000..a18ccab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute.go @@ -0,0 +1,87 @@ +package attribute + +import ( + "fmt" + "strings" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +const ( + documentDetailsDateFormatConst = "2006-01-02" +) + +// DocumentDetails represents information extracted from a document provided by the user +type DocumentDetails struct { + DocumentType string + IssuingCountry string + DocumentNumber string + ExpirationDate *time.Time + IssuingAuthority string +} + +// DocumentDetailsAttribute wraps a document details with anchor data +type DocumentDetailsAttribute struct { + attributeDetails + value DocumentDetails +} + +// Value returns the document details struct attached to this attribute +func (attr *DocumentDetailsAttribute) Value() DocumentDetails { + return attr.value +} + +// NewDocumentDetails creates a DocumentDetailsAttribute which wraps a +// DocumentDetails with anchor data +func NewDocumentDetails(a *yotiprotoattr.Attribute) (*DocumentDetailsAttribute, error) { + parsedAnchors := anchor.ParseAnchors(a.Anchors) + details := DocumentDetails{} + err := details.Parse(string(a.Value)) + if err != nil { + return nil, err + } + + return &DocumentDetailsAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: details, + }, nil +} + +// Parse fills a DocumentDetails object from a raw string +func (details *DocumentDetails) Parse(data string) error { + dataSlice := strings.Split(data, " ") + + if len(dataSlice) < 3 { + return fmt.Errorf("Document Details data is invalid, %s", data) + } + for _, section := range dataSlice { + if section == "" { + return fmt.Errorf("Document Details data is invalid %s", data) + } + } + + details.DocumentType = dataSlice[0] + details.IssuingCountry = dataSlice[1] + details.DocumentNumber = dataSlice[2] + if len(dataSlice) > 3 && dataSlice[3] != "-" { + expirationDateData, dateErr := time.Parse(documentDetailsDateFormatConst, dataSlice[3]) + + if dateErr == nil { + details.ExpirationDate = &expirationDateData + } else { + return dateErr + } + } + if len(dataSlice) > 4 { + details.IssuingAuthority = dataSlice[4] + } + + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute_test.go new file mode 100644 index 0000000..bf2e7de --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/document_details_attribute_test.go @@ -0,0 +1,185 @@ +package attribute + +import ( + "fmt" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func ExampleDocumentDetails_Parse() { + raw := "PASSPORT GBR 1234567 2022-09-12" + details := DocumentDetails{} + err := details.Parse(raw) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Printf( + "Document Type: %s, Issuing Country: %s, Document Number: %s, Expiration Date: %s", + details.DocumentType, + details.IssuingCountry, + details.DocumentNumber, + details.ExpirationDate, + ) + // Output: Document Type: PASSPORT, Issuing Country: GBR, Document Number: 1234567, Expiration Date: 2022-09-12 00:00:00 +0000 UTC +} + +func ExampleNewDocumentDetails() { + proto := yotiprotoattr.Attribute{ + Name: "exampleDocumentDetails", + Value: []byte("PASSPORT GBR 1234567 2022-09-12"), + ContentType: yotiprotoattr.ContentType_STRING, + } + attribute, err := NewDocumentDetails(&proto) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Printf( + "Document Type: %s, With %d Anchors", + attribute.Value().DocumentType, + len(attribute.Anchors()), + ) + // Output: Document Type: PASSPORT, With 0 Anchors +} + +func TestDocumentDetailsShouldParseDrivingLicenceWithoutExpiry(t *testing.T) { + drivingLicenceGBR := "PASS_CARD GBR 1234abc - DVLA" + + details := DocumentDetails{} + err := details.Parse(drivingLicenceGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "PASS_CARD") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Assert(t, details.ExpirationDate == nil) + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "DVLA") +} + +func TestDocumentDetailsShouldParseRedactedAadhar(t *testing.T) { + aadhaar := "AADHAAR IND ****1234 2016-05-01" + details := DocumentDetails{} + err := details.Parse(aadhaar) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "AADHAAR") + assert.Equal(t, details.DocumentNumber, "****1234") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "IND") + assert.Equal(t, details.IssuingAuthority, "") +} + +func TestDocumentDetailsShouldParseSpecialCharacters(t *testing.T) { + testData := [][]string{ + {"type country **** - authority", "****"}, + {"type country ~!@#$%^&*()-_=+[]{}|;':,./<>? - authority", "~!@#$%^&*()-_=+[]{}|;':,./<>?"}, + {"type country \"\" - authority", "\"\""}, + {"type country \\ - authority", "\\"}, + {"type country \" - authority", "\""}, + {"type country '' - authority", "''"}, + {"type country ' - authority", "'"}, + } + for _, row := range testData { + details := DocumentDetails{} + err := details.Parse(row[0]) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentNumber, row[1]) + } +} + +func TestDocumentDetailsShouldFailOnDoubleSpace(t *testing.T) { + data := "AADHAAR IND ****1234" + details := DocumentDetails{} + err := details.Parse(data) + assert.Check(t, err != nil) + assert.ErrorContains(t, err, "Document Details data is invalid") +} + +func TestDocumentDetailsShouldParseDrivingLicenceWithExtraAttribute(t *testing.T) { + drivingLicenceGBR := "DRIVING_LICENCE GBR 1234abc 2016-05-01 DVLA someThirdAttribute" + details := DocumentDetails{} + err := details.Parse(drivingLicenceGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "DRIVING_LICENCE") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "DVLA") +} + +func TestDocumentDetailsShouldParseDrivingLicenceWithAllOptionalAttributes(t *testing.T) { + drivingLicenceGBR := "DRIVING_LICENCE GBR 1234abc 2016-05-01 DVLA" + + details := DocumentDetails{} + err := details.Parse(drivingLicenceGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "DRIVING_LICENCE") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "DVLA") +} + +func TestDocumentDetailsShouldParseAadhaar(t *testing.T) { + aadhaar := "AADHAAR IND 1234abc 2016-05-01" + + details := DocumentDetails{} + err := details.Parse(aadhaar) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "AADHAAR") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Equal(t, details.ExpirationDate.Format("2006-01-02"), "2016-05-01") + assert.Equal(t, details.IssuingCountry, "IND") +} + +func TestDocumentDetailsShouldParsePassportWithMandatoryFieldsOnly(t *testing.T) { + passportGBR := "PASSPORT GBR 1234abc" + + details := DocumentDetails{} + err := details.Parse(passportGBR) + if err != nil { + t.Fail() + } + assert.Equal(t, details.DocumentType, "PASSPORT") + assert.Equal(t, details.DocumentNumber, "1234abc") + assert.Assert(t, details.ExpirationDate == nil) + assert.Equal(t, details.IssuingCountry, "GBR") + assert.Equal(t, details.IssuingAuthority, "") +} + +func TestDocumentDetailsShouldErrorOnEmptyString(t *testing.T) { + empty := "" + + details := DocumentDetails{} + err := details.Parse(empty) + assert.ErrorContains(t, err, "Document Details data is invalid") +} + +func TestDocumentDetailsShouldErrorIfLessThan3Words(t *testing.T) { + corrupt := "PASS_CARD GBR" + details := DocumentDetails{} + err := details.Parse(corrupt) + assert.ErrorContains(t, err, "Document Details data is invalid") +} + +func TestDocumentDetailsShouldErrorForInvalidExpirationDate(t *testing.T) { + corrupt := "PASSPORT GBR 1234abc X016-05-01" + details := DocumentDetails{} + err := details.Parse(corrupt) + assert.ErrorContains(t, err, "cannot parse") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute.go new file mode 100644 index 0000000..c729e30 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute.go @@ -0,0 +1,38 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// GenericAttribute is a Yoti attribute which returns a generic value +type GenericAttribute struct { + attributeDetails + value interface{} +} + +// NewGeneric creates a new generic attribute +func NewGeneric(a *yotiprotoattr.Attribute) *GenericAttribute { + value, err := parseValue(a.ContentType, a.Value) + + if err != nil { + return nil + } + + var parsedAnchors = anchor.ParseAnchors(a.Anchors) + + return &GenericAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: value, + } +} + +// Value returns the value of the GenericAttribute as an interface +func (a *GenericAttribute) Value() interface{} { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute_test.go new file mode 100644 index 0000000..e2daae8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/generic_attribute_test.go @@ -0,0 +1,39 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestNewGeneric_ShouldParseUnknownTypeAsString(t *testing.T) { + value := []byte("value") + protoAttr := yotiprotoattr.Attribute{ + ContentType: yotiprotoattr.ContentType_UNDEFINED, + Value: value, + } + parsed := NewGeneric(&protoAttr) + + stringValue, ok := parsed.Value().(string) + assert.Check(t, ok) + + assert.Equal(t, stringValue, string(value)) +} + +func TestGeneric_ContentType(t *testing.T) { + attribute := GenericAttribute{ + attributeDetails: attributeDetails{ + contentType: "contentType", + }, + } + + assert.Equal(t, attribute.ContentType(), "contentType") +} + +func TestNewGeneric_ShouldReturnNilForInvalidProtobuf(t *testing.T) { + invalid := NewGeneric(&yotiprotoattr.Attribute{ + ContentType: yotiprotoattr.ContentType_JSON, + }) + assert.Check(t, invalid == nil) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/helper_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/helper_test.go new file mode 100644 index 0000000..47c28ea --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/helper_test.go @@ -0,0 +1,21 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" +) + +func createAttributeFromTestFile(t *testing.T, filename string) *yotiprotoattr.Attribute { + attributeBytes := test.DecodeTestFile(t, filename) + + attributeStruct := &yotiprotoattr.Attribute{} + + err2 := proto.Unmarshal(attributeBytes, attributeStruct) + assert.NilError(t, err2) + + return attributeStruct +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute.go new file mode 100644 index 0000000..fd9d7f1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute.go @@ -0,0 +1,53 @@ +package attribute + +import ( + "errors" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// ImageAttribute is a Yoti attribute which returns an image as its value +type ImageAttribute struct { + attributeDetails + value media.Media +} + +// NewImage creates a new Image attribute +func NewImage(a *yotiprotoattr.Attribute) (*ImageAttribute, error) { + imageValue, err := parseImageValue(a.ContentType, a.Value) + if err != nil { + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &ImageAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: imageValue, + }, nil +} + +// Value returns the value of the ImageAttribute as media.Media +func (a *ImageAttribute) Value() media.Media { + return a.value +} + +func parseImageValue(contentType yotiprotoattr.ContentType, byteValue []byte) (media.Media, error) { + switch contentType { + case yotiprotoattr.ContentType_JPEG: + return media.JPEGImage(byteValue), nil + + case yotiprotoattr.ContentType_PNG: + return media.PNGImage(byteValue), nil + + default: + return nil, errors.New("cannot create Image with unsupported type") + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute_test.go new file mode 100644 index 0000000..2fe620f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_attribute_test.go @@ -0,0 +1,106 @@ +package attribute + +import ( + "encoding/base64" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestImageAttribute_Image_Png(t *testing.T) { + attributeName := consts.AttrSelfie + byteValue := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: byteValue, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + assert.DeepEqual(t, selfie.Value().Data(), byteValue) +} + +func TestImageAttribute_Image_Jpeg(t *testing.T) { + attributeName := consts.AttrSelfie + byteValue := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: byteValue, + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + assert.DeepEqual(t, selfie.Value().Data(), byteValue) +} + +func TestImageAttribute_Image_Default(t *testing.T) { + attributeName := consts.AttrSelfie + byteValue := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: byteValue, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + assert.DeepEqual(t, selfie.Value().Data(), byteValue) +} + +func TestImageAttribute_Base64Selfie_Png(t *testing.T) { + attributeName := consts.AttrSelfie + imageBytes := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: imageBytes, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(imageBytes) + + expectedBase64Selfie := "data:image/png;base64," + base64ImageExpectedValue + + base64Selfie := selfie.Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} + +func TestImageAttribute_Base64URL_Jpeg(t *testing.T) { + attributeName := consts.AttrSelfie + imageBytes := []byte("value") + + var attributeImage = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: imageBytes, + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + selfie, err := NewImage(attributeImage) + assert.NilError(t, err) + + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(imageBytes) + + expectedBase64Selfie := "data:image/jpeg;base64," + base64ImageExpectedValue + + base64Selfie := selfie.Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute.go new file mode 100644 index 0000000..de507ab --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute.go @@ -0,0 +1,69 @@ +package attribute + +import ( + "errors" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// ImageSliceAttribute is a Yoti attribute which returns a slice of images as its value +type ImageSliceAttribute struct { + attributeDetails + value []media.Media +} + +// NewImageSlice creates a new ImageSlice attribute +func NewImageSlice(a *yotiprotoattr.Attribute) (*ImageSliceAttribute, error) { + if a.ContentType != yotiprotoattr.ContentType_MULTI_VALUE { + return nil, errors.New("creating an Image Slice attribute with content types other than MULTI_VALUE is not supported") + } + + parsedMultiValue, err := parseMultiValue(a.Value) + + if err != nil { + return nil, err + } + + var imageSliceValue []media.Media + if parsedMultiValue != nil { + imageSliceValue, err = CreateImageSlice(parsedMultiValue) + if err != nil { + return nil, err + } + } + + return &ImageSliceAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: anchor.ParseAnchors(a.Anchors), + id: &a.EphemeralId, + }, + value: imageSliceValue, + }, nil +} + +// CreateImageSlice takes a slice of Items, and converts them into a slice of images +func CreateImageSlice(items []*Item) (result []media.Media, err error) { + for _, item := range items { + + switch i := item.Value.(type) { + case media.PNGImage: + result = append(result, i) + case media.JPEGImage: + result = append(result, i) + default: + return nil, fmt.Errorf("unexpected item type %T", i) + } + } + + return result, nil +} + +// Value returns the value of the ImageSliceAttribute +func (a *ImageSliceAttribute) Value() []media.Media { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute_test.go new file mode 100644 index 0000000..2c30092 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/image_slice_attribute_test.go @@ -0,0 +1,61 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func assertIsExpectedImage(t *testing.T, image media.Media, imageMIMEType string, expectedBase64URLLast10 string) { + assert.Equal(t, image.MIME(), imageMIMEType) + + actualBase64URL := image.Base64URL() + + ActualBase64URLLast10Chars := actualBase64URL[len(actualBase64URL)-10:] + + assert.Equal(t, ActualBase64URLLast10Chars, expectedBase64URLLast10) +} + +func assertIsExpectedDocumentImagesAttribute(t *testing.T, actualDocumentImages []media.Media, anchor *anchor.Anchor) { + + assert.Equal(t, len(actualDocumentImages), 2, "This Document Images attribute should have two images") + + assertIsExpectedImage(t, actualDocumentImages[0], media.ImageTypeJPEG, "vWgD//2Q==") + assertIsExpectedImage(t, actualDocumentImages[1], media.ImageTypeJPEG, "38TVEH/9k=") + + expectedValue := "NATIONAL_ID" + assert.Equal(t, anchor.Value(), expectedValue) + + expectedSubType := "STATE_ID" + assert.Equal(t, anchor.SubType(), expectedSubType) +} + +func TestAttribute_NewImageSlice(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt") + + documentImagesAttribute, err := NewImageSlice(protoAttribute) + + assert.NilError(t, err) + + assertIsExpectedDocumentImagesAttribute(t, documentImagesAttribute.Value(), documentImagesAttribute.Anchors()[0]) +} + +func TestAttribute_ImageSliceNotCreatedWithNonMultiValueType(t *testing.T) { + attributeName := "attributeName" + attributeValueString := "value" + attributeValue := []byte(attributeValueString) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + _, err := NewImageSlice(attr) + + assert.Assert(t, err != nil, "Expected error when creating image slice from attribute which isn't of multi-value type") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details.go new file mode 100644 index 0000000..381d4e9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details.go @@ -0,0 +1,86 @@ +package attribute + +import ( + "encoding/base64" + "errors" + "fmt" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" +) + +// IssuanceDetails contains information about the attribute(s) issued by a third party +type IssuanceDetails struct { + token string + expiryDate *time.Time + attributes []Definition +} + +// Token is the issuance token that can be used to retrieve the user's stored details. +// These details will be used to issue attributes on behalf of an organisation to that user. +func (i IssuanceDetails) Token() string { + return i.token +} + +// ExpiryDate is the timestamp at which the request for the attribute value +// from third party will expire. Will be nil if not provided. +func (i IssuanceDetails) ExpiryDate() *time.Time { + return i.expiryDate +} + +// Attributes information about the attributes the third party would like to issue. +func (i IssuanceDetails) Attributes() []Definition { + return i.attributes +} + +// ParseIssuanceDetails takes the Third Party Attribute object and converts it into an IssuanceDetails struct +func ParseIssuanceDetails(thirdPartyAttributeBytes []byte) (*IssuanceDetails, error) { + thirdPartyAttributeStruct := &yotiprotoshare.ThirdPartyAttribute{} + if err := proto.Unmarshal(thirdPartyAttributeBytes, thirdPartyAttributeStruct); err != nil { + return nil, fmt.Errorf("unable to parse ThirdPartyAttribute value: %q. Error: %q", string(thirdPartyAttributeBytes), err) + } + + var issuingAttributesProto = thirdPartyAttributeStruct.GetIssuingAttributes() + var issuingAttributeDefinitions = parseIssuingAttributeDefinitions(issuingAttributesProto.GetDefinitions()) + + expiryDate, dateParseErr := parseExpiryDate(issuingAttributesProto.ExpiryDate) + + var issuanceTokenBytes = thirdPartyAttributeStruct.GetIssuanceToken() + + if len(issuanceTokenBytes) == 0 { + return nil, errors.New("Issuance Token is invalid") + } + + base64EncodedToken := base64.StdEncoding.EncodeToString(issuanceTokenBytes) + + return &IssuanceDetails{ + token: base64EncodedToken, + expiryDate: expiryDate, + attributes: issuingAttributeDefinitions, + }, dateParseErr +} + +func parseIssuingAttributeDefinitions(definitions []*yotiprotoshare.Definition) (issuingAttributes []Definition) { + for _, definition := range definitions { + attributeDefinition := Definition{ + name: definition.Name, + } + issuingAttributes = append(issuingAttributes, attributeDefinition) + } + + return issuingAttributes +} + +func parseExpiryDate(expiryDateString string) (*time.Time, error) { + if expiryDateString == "" { + return nil, nil + } + + parsedTime, err := time.Parse(time.RFC3339Nano, expiryDateString) + if err != nil { + return nil, err + } + + return &parsedTime, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details_test.go new file mode 100644 index 0000000..462d863 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/issuance_details_test.go @@ -0,0 +1,145 @@ +package attribute + +import ( + "encoding/base64" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/test" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" + + "gotest.tools/v3/assert" + + is "gotest.tools/v3/assert/cmp" +) + +func TestShouldParseThirdPartyAttributeCorrectly(t *testing.T) { + var thirdPartyAttributeBytes = test.GetTestFileBytes(t, "../../test/fixtures/test_third_party_issuance_details.txt") + issuanceDetails, err := ParseIssuanceDetails(thirdPartyAttributeBytes) + + assert.NilError(t, err) + assert.Equal(t, issuanceDetails.Attributes()[0].Name(), "com.thirdparty.id") + assert.Equal(t, issuanceDetails.Token(), "c29tZUlzc3VhbmNlVG9rZW4=") + assert.Equal(t, + issuanceDetails.ExpiryDate().Format("2006-01-02T15:04:05.000Z"), + "2019-10-15T22:04:05.123Z") +} + +func TestShouldLogWarningIfErrorInParsingExpiryDate(t *testing.T) { + var tokenValue = "41548a175dfaw" + thirdPartyAttribute := &yotiprotoshare.ThirdPartyAttribute{ + IssuanceToken: []byte(tokenValue), + IssuingAttributes: &yotiprotoshare.IssuingAttributes{ + ExpiryDate: "2006-13-02T15:04:05.000Z", + }, + } + + marshalled, err := proto.Marshal(thirdPartyAttribute) + + assert.NilError(t, err) + + var tokenBytes = []byte(tokenValue) + var expectedBase64Token = base64.StdEncoding.EncodeToString(tokenBytes) + + result, err := ParseIssuanceDetails(marshalled) + assert.Equal(t, expectedBase64Token, result.Token()) + assert.Assert(t, is.Nil(result.ExpiryDate())) + assert.Equal(t, "parsing time \"2006-13-02T15:04:05.000Z\": month out of range", err.Error()) +} + +func TestIssuanceDetails_parseExpiryDate_ShouldParseAllRFC3339Formats(t *testing.T) { + table := []struct { + Input string + Expected time.Time + }{ + { + Input: "2006-01-02T22:04:05Z", + Expected: time.Date(2006, 01, 02, 22, 4, 5, 0, time.UTC), + }, + { + Input: "2010-05-20T10:44:25Z", + Expected: time.Date(2010, 5, 20, 10, 44, 25, 0, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.1Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 100e6, time.UTC), + }, + { + Input: "2012-03-06T04:20:07.5Z", + Expected: time.Date(2012, 3, 6, 4, 20, 7, 500e6, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.12Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 120e6, time.UTC), + }, + { + Input: "2013-03-04T20:43:55.56Z", + Expected: time.Date(2013, 3, 4, 20, 43, 55, 560e6, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.123Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123e6, time.UTC), + }, + { + Input: "2007-04-07T17:34:11.784Z", + Expected: time.Date(2007, 4, 7, 17, 34, 11, 784e6, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.1234Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123400e3, time.UTC), + }, + { + Input: "2017-09-14T16:54:30.4784Z", + Expected: time.Date(2017, 9, 14, 16, 54, 30, 478400e3, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.12345Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123450e3, time.UTC), + }, + { + Input: "2009-06-07T14:20:30.74622Z", + Expected: time.Date(2009, 6, 7, 14, 20, 30, 746220e3, time.UTC), + }, + { + Input: "2006-01-02T22:04:05.123456Z", + Expected: time.Date(2006, 1, 2, 22, 4, 5, 123456e3, time.UTC), + }, + { + Input: "2008-10-25T06:50:55.643562Z", + Expected: time.Date(2008, 10, 25, 6, 50, 55, 643562e3, time.UTC), + }, + { + Input: "2002-10-02T10:00:00-05:00", + Expected: time.Date(2002, 10, 2, 10, 0, 0, 0, time.FixedZone("-0500", -5*60*60)), + }, + { + Input: "2002-10-02T10:00:00+11:00", + Expected: time.Date(2002, 10, 2, 10, 0, 0, 0, time.FixedZone("+1100", 11*60*60)), + }, + { + Input: "1920-03-13T19:50:53.999999Z", + Expected: time.Date(1920, 3, 13, 19, 50, 53, 999999e3, time.UTC), + }, + { + Input: "1920-03-13T19:50:54.000001Z", + Expected: time.Date(1920, 3, 13, 19, 50, 54, 1e3, time.UTC), + }, + } + + for _, row := range table { + func(input string, expected time.Time) { + expiryDate, err := parseExpiryDate(input) + assert.NilError(t, err) + assert.Equal(t, expiryDate.UTC(), expected.UTC()) + }(row.Input, row.Expected) + } +} + +func TestInvalidProtobufThrowsError(t *testing.T) { + result, err := ParseIssuanceDetails([]byte("invalid")) + + assert.Assert(t, is.Nil(result)) + + assert.ErrorContains(t, err, "unable to parse ThirdPartyAttribute value") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/item.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/item.go new file mode 100644 index 0000000..3efd2b9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/item.go @@ -0,0 +1,14 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// Item is a structure which contains information about an attribute value +type Item struct { + // ContentType is the content of the item. + ContentType yotiprotoattr.ContentType + + // Value is the underlying data of the item. + Value interface{} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute.go new file mode 100644 index 0000000..be40920 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute.go @@ -0,0 +1,58 @@ +package attribute + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// JSONAttribute is a Yoti attribute which returns an interface as its value +type JSONAttribute struct { + attributeDetails + // value returns the value of a JSON attribute in the form of an interface + value map[string]interface{} +} + +// NewJSON creates a new JSON attribute +func NewJSON(a *yotiprotoattr.Attribute) (*JSONAttribute, error) { + var interfaceValue map[string]interface{} + decoder := json.NewDecoder(bytes.NewReader(a.Value)) + decoder.UseNumber() + err := decoder.Decode(&interfaceValue) + if err != nil { + err = fmt.Errorf("unable to parse JSON value: %q. Error: %q", a.Value, err) + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &JSONAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: interfaceValue, + }, nil +} + +// unmarshallJSON unmarshalls JSON into an interface +func unmarshallJSON(byteValue []byte) (result map[string]interface{}, err error) { + var unmarshalledJSON map[string]interface{} + err = json.Unmarshal(byteValue, &unmarshalledJSON) + + if err != nil { + return nil, err + } + + return unmarshalledJSON, err +} + +// Value returns the value of the JSONAttribute as an interface. +func (a *JSONAttribute) Value() map[string]interface{} { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute_test.go new file mode 100644 index 0000000..4275637 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/json_attribute_test.go @@ -0,0 +1,76 @@ +package attribute + +import ( + "fmt" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func ExampleNewJSON() { + proto := yotiprotoattr.Attribute{ + Name: "exampleJSON", + Value: []byte(`{"foo":"bar"}`), + ContentType: yotiprotoattr.ContentType_JSON, + } + attribute, err := NewJSON(&proto) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + fmt.Println(attribute.Value()) + // Output: map[foo:bar] +} + +func TestNewJSON_ShouldReturnNilForInvalidJSON(t *testing.T) { + proto := yotiprotoattr.Attribute{ + Name: "exampleJSON", + Value: []byte("Not a json document"), + ContentType: yotiprotoattr.ContentType_JSON, + } + attribute, err := NewJSON(&proto) + assert.Check(t, attribute == nil) + assert.ErrorContains(t, err, "unable to parse JSON value") +} + +func TestYotiClient_UnmarshallJSONValue_InvalidValueThrowsError(t *testing.T) { + invalidStructuredAddress := []byte("invalidBool") + + _, err := unmarshallJSON(invalidStructuredAddress) + + assert.Assert(t, err != nil) +} + +func TestYotiClient_UnmarshallJSONValue_ValidValue(t *testing.T) { + const ( + countryIso = "IND" + nestedValue = "NestedValue" + ) + + var structuredAddress = []byte(` + { + "address_format": 2, + "building": "House No.86-A", + "state": "Punjab", + "postal_code": "141012", + "country_iso": "` + countryIso + `", + "country": "India", + "formatted_address": "House No.86-A\nRajgura Nagar\nLudhina\nPunjab\n141012\nIndia", + "1": + { + "1-1": + { + "1-1-1": "` + nestedValue + `" + } + } + } + `) + + parsedStructuredAddress, err := unmarshallJSON(structuredAddress) + assert.NilError(t, err, "Failed to parse structured address") + + actualCountryIso := parsedStructuredAddress["country_iso"] + + assert.Equal(t, countryIso, actualCountryIso) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute.go new file mode 100644 index 0000000..926141f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute.go @@ -0,0 +1,90 @@ +package attribute + +import ( + "fmt" + + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" +) + +// MultiValueAttribute is a Yoti attribute which returns a multi-valued attribute +type MultiValueAttribute struct { + attributeDetails + items []*Item +} + +// NewMultiValue creates a new MultiValue attribute +func NewMultiValue(a *yotiprotoattr.Attribute) (*MultiValueAttribute, error) { + attributeItems, err := parseMultiValue(a.Value) + + if err != nil { + return nil, err + } + + return &MultiValueAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: anchor.ParseAnchors(a.Anchors), + id: &a.EphemeralId, + }, + items: attributeItems, + }, nil +} + +// parseMultiValue recursively unmarshals and converts Multi Value bytes into a slice of Items +func parseMultiValue(data []byte) ([]*Item, error) { + var attributeItems []*Item + protoMultiValueStruct, err := unmarshallMultiValue(data) + + if err != nil { + return nil, err + } + + for _, multiValueItem := range protoMultiValueStruct.Values { + var value *Item + if multiValueItem.ContentType == yotiprotoattr.ContentType_MULTI_VALUE { + parsedInnerMultiValueItems, err := parseMultiValue(multiValueItem.Data) + + if err != nil { + return nil, fmt.Errorf("unable to parse multi-value data: %v", err) + } + + value = &Item{ + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Value: parsedInnerMultiValueItems, + } + } else { + itemValue, err := parseValue(multiValueItem.ContentType, multiValueItem.Data) + + if err != nil { + return nil, fmt.Errorf("unable to parse data within a multi-value attribute. Content type: %q, data: %q, error: %v", + multiValueItem.ContentType, multiValueItem.Data, err) + } + + value = &Item{ + ContentType: multiValueItem.ContentType, + Value: itemValue, + } + } + attributeItems = append(attributeItems, value) + } + + return attributeItems, nil +} + +func unmarshallMultiValue(bytes []byte) (*yotiprotoattr.MultiValue, error) { + multiValueStruct := &yotiprotoattr.MultiValue{} + + if err := proto.Unmarshal(bytes, multiValueStruct); err != nil { + return nil, fmt.Errorf("unable to parse MULTI_VALUE value: %q. Error: %q", string(bytes), err) + } + + return multiValueStruct, nil +} + +// Value returns the value of the MultiValueAttribute as a string +func (a *MultiValueAttribute) Value() []*Item { + return a.items +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute_test.go new file mode 100644 index 0000000..15a24f9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/multivalue_attribute_test.go @@ -0,0 +1,157 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func marshallMultiValue(t *testing.T, multiValue *yotiprotoattr.MultiValue) []byte { + marshalled, err := proto.Marshal(multiValue) + + assert.NilError(t, err) + + return marshalled +} + +func createMultiValueAttribute(t *testing.T, multiValueItemSlice []*yotiprotoattr.MultiValue_Value) (*MultiValueAttribute, error) { + var multiValueStruct = &yotiprotoattr.MultiValue{ + Values: multiValueItemSlice, + } + + var marshalledMultiValueData = marshallMultiValue(t, multiValueStruct) + attributeName := "nestedMultiValue" + + var protoAttribute = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: marshalledMultiValueData, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: []*yotiprotoattr.Anchor{}, + } + + return NewMultiValue(protoAttribute) +} + +func TestAttribute_MultiValueNotCreatedWithNonMultiValueType(t *testing.T) { + attributeName := "attributeName" + attributeValueString := "value" + attributeValue := []byte(attributeValueString) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + _, err := NewMultiValue(attr) + + assert.Assert(t, err != nil, "Expected error when creating multi value from attribute which isn't of multi-value type") +} + +func TestAttribute_NewMultiValue(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt") + + multiValueAttribute, err := NewMultiValue(protoAttribute) + + assert.NilError(t, err) + + documentImagesAttributeItems, err := CreateImageSlice(multiValueAttribute.Value()) + assert.NilError(t, err) + + assertIsExpectedDocumentImagesAttribute(t, documentImagesAttributeItems, multiValueAttribute.Anchors()[0]) +} + +func TestAttribute_InvalidMultiValueNotReturned(t *testing.T) { + var invalidMultiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_DATE, + Data: []byte("invalid"), + } + + var stringMultiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_STRING, + Data: []byte("string"), + } + + var multiValueItemSlice = []*yotiprotoattr.MultiValue_Value{invalidMultiValueItem, stringMultiValueItem} + + var multiValueStruct = &yotiprotoattr.MultiValue{ + Values: multiValueItemSlice, + } + + var marshalledMultiValueData = marshallMultiValue(t, multiValueStruct) + attributeName := "nestedMultiValue" + + var protoAttribute = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: marshalledMultiValueData, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: []*yotiprotoattr.Anchor{}, + } + + multiValueAttr, err := NewMultiValue(protoAttribute) + assert.Check(t, err != nil) + + assert.Assert(t, is.Nil(multiValueAttr)) +} + +func TestAttribute_NestedMultiValue(t *testing.T) { + var innerMultiValueProtoValue = createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt").Value + + var stringMultiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_STRING, + Data: []byte("string"), + } + + var multiValueItem = &yotiprotoattr.MultiValue_Value{ + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Data: innerMultiValueProtoValue, + } + + var multiValueItemSlice = []*yotiprotoattr.MultiValue_Value{stringMultiValueItem, multiValueItem} + + multiValueAttribute, err := createMultiValueAttribute(t, multiValueItemSlice) + + assert.NilError(t, err) + + for key, value := range multiValueAttribute.Value() { + switch key { + case 0: + value0 := value.Value + + assert.Equal(t, value0.(string), "string") + case 1: + value1 := value.Value + + innerItems, ok := value1.([]*Item) + assert.Assert(t, ok) + + for innerKey, item := range innerItems { + switch innerKey { + case 0: + assertIsExpectedImage(t, item.Value.(media.Media), media.ImageTypeJPEG, "vWgD//2Q==") + + case 1: + assertIsExpectedImage(t, item.Value.(media.Media), media.ImageTypeJPEG, "38TVEH/9k=") + } + } + } + } +} + +func TestAttribute_MultiValueGenericGetter(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_multivalue.txt") + multiValueAttribute, err := NewMultiValue(protoAttribute) + assert.NilError(t, err) + + // We need to cast, since GetAttribute always returns generic attributes + multiValueAttributeValue := multiValueAttribute.Value() + imageSlice, err := CreateImageSlice(multiValueAttributeValue) + assert.NilError(t, err) + + assertIsExpectedDocumentImagesAttribute(t, imageSlice, multiValueAttribute.Anchors()[0]) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser.go new file mode 100644 index 0000000..d663595 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser.go @@ -0,0 +1,56 @@ +package attribute + +import ( + "fmt" + "strconv" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +func parseValue(contentType yotiprotoattr.ContentType, byteValue []byte) (interface{}, error) { + switch contentType { + case yotiprotoattr.ContentType_DATE: + parsedTime, err := time.Parse("2006-01-02", string(byteValue)) + + if err == nil { + return &parsedTime, nil + } + + return nil, fmt.Errorf("unable to parse date value: %q. Error: %q", string(byteValue), err) + + case yotiprotoattr.ContentType_JSON: + unmarshalledJSON, err := unmarshallJSON(byteValue) + + if err == nil { + return unmarshalledJSON, nil + } + + return nil, fmt.Errorf("unable to parse JSON value: %q. Error: %q", string(byteValue), err) + + case yotiprotoattr.ContentType_STRING: + return string(byteValue), nil + + case yotiprotoattr.ContentType_MULTI_VALUE: + return parseMultiValue(byteValue) + + case yotiprotoattr.ContentType_INT: + var stringValue = string(byteValue) + intValue, err := strconv.Atoi(stringValue) + if err == nil { + return intValue, nil + } + + return nil, fmt.Errorf("unable to parse INT value: %q. Error: %q", string(byteValue), err) + + case yotiprotoattr.ContentType_JPEG, + yotiprotoattr.ContentType_PNG: + return parseImageValue(contentType, byteValue) + + case yotiprotoattr.ContentType_UNDEFINED: + return string(byteValue), nil + + default: + return string(byteValue), nil + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser_test.go new file mode 100644 index 0000000..cc9f3d8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/parser_test.go @@ -0,0 +1,16 @@ +package attribute + +import ( + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "gotest.tools/v3/assert" +) + +func TestParseValue_ShouldParseInt(t *testing.T) { + parsed, err := parseValue(yotiprotoattr.ContentType_INT, []byte("7")) + assert.NilError(t, err) + integer, ok := parsed.(int) + assert.Check(t, ok) + assert.Equal(t, integer, 7) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute.go new file mode 100644 index 0000000..73346b9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute.go @@ -0,0 +1,32 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute/anchor" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// StringAttribute is a Yoti attribute which returns a string as its value +type StringAttribute struct { + attributeDetails + value string +} + +// NewString creates a new String attribute +func NewString(a *yotiprotoattr.Attribute) *StringAttribute { + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &StringAttribute{ + attributeDetails: attributeDetails{ + name: a.Name, + contentType: a.ContentType.String(), + anchors: parsedAnchors, + id: &a.EphemeralId, + }, + value: string(a.Value), + } +} + +// Value returns the value of the StringAttribute as a string +func (a *StringAttribute) Value() string { + return a.value +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute_test.go new file mode 100644 index 0000000..828df20 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/attribute/string_attribute_test.go @@ -0,0 +1,22 @@ +package attribute + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestStringAttribute_NewThirdPartyAttribute(t *testing.T) { + protoAttribute := createAttributeFromTestFile(t, "../../test/fixtures/test_attribute_third_party.txt") + + stringAttribute := NewString(protoAttribute) + + assert.Equal(t, stringAttribute.Value(), "test-third-party-attribute-0") + assert.Equal(t, stringAttribute.Name(), "com.thirdparty.id") + + assert.Equal(t, stringAttribute.Sources()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Sources()[0].SubType(), "orgName") + + assert.Equal(t, stringAttribute.Verifiers()[0].Value(), "THIRD_PARTY") + assert.Equal(t, stringAttribute.Verifiers()[0].SubType(), "orgName") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/base_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/base_profile.go new file mode 100644 index 0000000..df5ad75 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/base_profile.go @@ -0,0 +1,75 @@ +package profile + +import ( + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +type baseProfile struct { + attributeSlice []*yotiprotoattr.Attribute +} + +// GetAttribute retrieve an attribute by name on the Yoti profile. Will return nil if attribute is not present. +func (p baseProfile) GetAttribute(attributeName string) *attribute.GenericAttribute { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + return attribute.NewGeneric(a) + } + } + return nil +} + +// GetAttributeByID retrieve an attribute by ID on the Yoti profile. Will return nil if attribute is not present. +func (p baseProfile) GetAttributeByID(attributeID string) *attribute.GenericAttribute { + for _, a := range p.attributeSlice { + if a.EphemeralId == attributeID { + return attribute.NewGeneric(a) + } + } + return nil +} + +// GetAttributes retrieve a list of attributes by name on the Yoti profile. Will return an empty list of attribute is not present. +func (p baseProfile) GetAttributes(attributeName string) []*attribute.GenericAttribute { + var attributes []*attribute.GenericAttribute + for _, a := range p.attributeSlice { + if a.Name == attributeName { + attributes = append(attributes, attribute.NewGeneric(a)) + } + } + return attributes +} + +// GetStringAttribute retrieves a string attribute by name. Will return nil if attribute is not present. +func (p baseProfile) GetStringAttribute(attributeName string) *attribute.StringAttribute { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + return attribute.NewString(a) + } + } + return nil +} + +// GetImageAttribute retrieves an image attribute by name. Will return nil if attribute is not present. +func (p baseProfile) GetImageAttribute(attributeName string) *attribute.ImageAttribute { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + imageAttribute, err := attribute.NewImage(a) + + if err == nil { + return imageAttribute + } + } + } + return nil +} + +// GetJSONAttribute retrieves a JSON attribute by name. Will return nil if attribute is not present. +func (p baseProfile) GetJSONAttribute(attributeName string) (*attribute.JSONAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == attributeName { + return attribute.NewJSON(a) + } + } + return nil, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/data_objects.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/data_objects.go new file mode 100644 index 0000000..27d4b65 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/data_objects.go @@ -0,0 +1,27 @@ +package profile + +type receiptDO struct { + ReceiptID string `json:"receipt_id"` + OtherPartyProfileContent string `json:"other_party_profile_content"` + ProfileContent string `json:"profile_content"` + OtherPartyExtraDataContent string `json:"other_party_extra_data_content"` + ExtraDataContent string `json:"extra_data_content"` + WrappedReceiptKey string `json:"wrapped_receipt_key"` + PolicyURI string `json:"policy_uri"` + PersonalKey string `json:"personal_key"` + RememberMeID string `json:"remember_me_id"` + ParentRememberMeID string `json:"parent_remember_me_id"` + SharingOutcome string `json:"sharing_outcome"` + Timestamp string `json:"timestamp"` +} + +type errorDetailsDO struct { + ErrorCode *string `json:"error_code"` + Description *string `json:"description"` +} + +type profileDO struct { + SessionData string `json:"session_data"` + Receipt receiptDO `json:"receipt"` + ErrorDetails *errorDetailsDO `json:"error_details"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/receipt_parser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/receipt_parser.go new file mode 100644 index 0000000..5fff5a0 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/receipt_parser.go @@ -0,0 +1,61 @@ +package profile + +import ( + "crypto/rsa" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/util" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" + "google.golang.org/protobuf/proto" +) + +func parseApplicationProfile(receipt *receiptDO, key *rsa.PrivateKey) (result *yotiprotoattr.AttributeList, err error) { + decipheredBytes, err := parseEncryptedProto(receipt, receipt.ProfileContent, key) + if err != nil { + return + } + + attributeList := &yotiprotoattr.AttributeList{} + if err := proto.Unmarshal(decipheredBytes, attributeList); err != nil { + return nil, err + } + + return attributeList, nil +} + +func parseUserProfile(receipt *receiptDO, key *rsa.PrivateKey) (result *yotiprotoattr.AttributeList, err error) { + decipheredBytes, err := parseEncryptedProto(receipt, receipt.OtherPartyProfileContent, key) + if err != nil { + return + } + + attributeList := &yotiprotoattr.AttributeList{} + if err := proto.Unmarshal(decipheredBytes, attributeList); err != nil { + return nil, err + } + + return attributeList, nil +} + +func decryptExtraData(receipt *receiptDO, key *rsa.PrivateKey) (result []byte, err error) { + bytes, err := parseEncryptedProto(receipt, receipt.ExtraDataContent, key) + return bytes, err +} + +func parseEncryptedProto(receipt *receiptDO, encryptedBase64 string, key *rsa.PrivateKey) (result []byte, err error) { + unwrappedKey, err := cryptoutil.UnwrapKey(receipt.WrappedReceiptKey, key) + if err != nil { + return + } + encryptedBytes, err := util.Base64ToBytes(encryptedBase64) + if err != nil || len(encryptedBytes) == 0 { + return + } + encryptedData := &yotiprotocom.EncryptedData{} + if err = proto.Unmarshal(encryptedBytes, encryptedData); err != nil { + return + } + + return cryptoutil.DecipherAes(unwrappedKey, encryptedData.Iv, encryptedData.CipherText) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor.go new file mode 100644 index 0000000..ee630c4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor.go @@ -0,0 +1,62 @@ +package sandbox + +import ( + "encoding/json" + "time" +) + +// SourceAnchor initialises an anchor where the type is "SOURCE", +// which has information about how the anchor was sourced. +func SourceAnchor(subtype string, timestamp time.Time, value string) Anchor { + return Anchor{ + Type: "SOURCE", + Value: value, + SubType: subtype, + Timestamp: timestamp, + } +} + +// VerifierAnchor initialises an anchor where the type is "VERIFIER", +// which has information about how the anchor was verified. +func VerifierAnchor(subtype string, timestamp time.Time, value string) Anchor { + return Anchor{ + Type: "VERIFIER", + Value: value, + SubType: subtype, + Timestamp: timestamp, + } +} + +// Anchor is the metadata associated with an attribute. +// It describes how an attribute has been provided to Yoti +// (SOURCE Anchor) and how it has been verified (VERIFIER Anchor). +type Anchor struct { + // Type of the Anchor - most likely either SOURCE or VERIFIER, but it's + // possible that new Anchor types will be added in future. + Type string + // Value identifies the provider that either sourced or verified the attribute value. + // The range of possible values is not limited. For a SOURCE anchor, expect values like + // PASSPORT, DRIVING_LICENSE. For a VERIFIER anchor expect values like YOTI_ADMIN. + Value string + // SubType is an indicator of any specific processing method, or subcategory, + // pertaining to an artifact. For example, for a passport, this would be + // either "NFC" or "OCR". + SubType string + // Timestamp is the time when the anchor was created, i.e. when it was SOURCED or VERIFIED. + Timestamp time.Time +} + +// MarshalJSON returns the JSON encoding +func (anchor *Anchor) MarshalJSON() ([]byte, error) { + return json.Marshal(&struct { + Type string `json:"type"` + Value string `json:"value"` + SubType string `json:"sub_type"` + Timestamp int64 `json:"timestamp"` + }{ + Type: anchor.Type, + Value: anchor.Value, + SubType: anchor.SubType, + Timestamp: anchor.Timestamp.UnixNano() / 1e6, + }) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor_test.go new file mode 100644 index 0000000..ee62129 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/anchor_test.go @@ -0,0 +1,32 @@ +package sandbox + +import ( + "fmt" + "time" +) + +func ExampleSourceAnchor() { + time.Local = time.UTC + source := SourceAnchor("subtype", time.Unix(1234567890, 0), "value") + marshalled, err := source.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalled)) + // Output: {"type":"SOURCE","value":"value","sub_type":"subtype","timestamp":1234567890000} +} + +func ExampleVerifierAnchor() { + time.Local = time.UTC + verifier := VerifierAnchor("subtype", time.Unix(1234567890, 0), "value") + marshalled, err := verifier.MarshalJSON() + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalled)) + // Output: {"type":"VERIFIER","value":"value","sub_type":"subtype","timestamp":1234567890000} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute.go new file mode 100644 index 0000000..bf9fbf2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute.go @@ -0,0 +1,52 @@ +package sandbox + +import "strconv" + +// Attribute describes an attribute on a sandbox profile +type Attribute struct { + Name string `json:"name"` + Value string `json:"value"` + Derivation string `json:"derivation"` + Optional string `json:"optional"` + Anchors []Anchor `json:"anchors"` +} + +// Derivation is a builder for derivation strings +type Derivation struct { + value string +} + +// WithName sets the value of a Sandbox Attribute +func (attr Attribute) WithName(name string) Attribute { + attr.Name = name + return attr +} + +// WithValue sets the value of a Sandbox Attribute +func (attr Attribute) WithValue(value string) Attribute { + attr.Value = value + return attr +} + +// WithAnchor sets the Anchor of a Sandbox Attribute +func (attr Attribute) WithAnchor(anchor Anchor) Attribute { + attr.Anchors = append(attr.Anchors, anchor) + return attr +} + +// ToString returns the string representation for a derivation +func (derivation Derivation) ToString() string { + return derivation.value +} + +// AgeOver builds an age over age derivation +func (derivation Derivation) AgeOver(age int) Derivation { + derivation.value = "age_over:" + strconv.Itoa(age) + return derivation +} + +// AgeUnder builds an age under age derivation +func (derivation Derivation) AgeUnder(age int) Derivation { + derivation.value = "age_under:" + strconv.Itoa(age) + return derivation +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute_test.go new file mode 100644 index 0000000..82a2f5b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/attribute_test.go @@ -0,0 +1,48 @@ +package sandbox + +import ( + "fmt" + "testing" + "time" + + "gotest.tools/v3/assert" +) + +func ExampleAttribute_WithAnchor() { + time.Local = time.UTC + attribute := Attribute{}.WithAnchor(SourceAnchor("", time.Unix(1234567890, 0), "")) + fmt.Print(attribute) + // Output: { [{SOURCE 2009-02-13 23:31:30 +0000 UTC}]} +} + +func TestAttribute_WithName(t *testing.T) { + attribute := Attribute{}.WithName("attribute_name") + + assert.Equal(t, attribute.Name, "attribute_name") +} + +func TestAttribute_WithValue(t *testing.T) { + attribute := Attribute{}.WithValue("Value") + + assert.Equal(t, attribute.Value, "Value") +} + +func ExampleDerivation_AgeOver() { + attribute := Attribute{ + Name: "date_of_birth", + Value: "Value", + Derivation: Derivation{}.AgeOver(18).ToString(), + } + fmt.Println(attribute) + // Output: {date_of_birth Value age_over:18 []} +} + +func ExampleDerivation_AgeUnder() { + attribute := Attribute{ + Name: "date_of_birth", + Value: "Value", + Derivation: Derivation{}.AgeUnder(14).ToString(), + } + fmt.Println(attribute) + // Output: {date_of_birth Value age_under:14 []} +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client.go new file mode 100644 index 0000000..9cbde3b --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client.go @@ -0,0 +1,80 @@ +package sandbox + +import ( + "crypto/rsa" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + yotirequest "github.com/getyoti/yoti-go-sdk/v3/requests" +) + +// Client is responsible for setting up test data in the sandbox instance. BaseURL is not required. +type Client struct { + // Client SDK ID. This can be found in the Yoti Hub after you have created and activated an application. + ClientSdkID string + // Private Key associated for your application, can be downloaded from the Yoti Hub. + Key *rsa.PrivateKey + // Base URL to use. This is not required, and a default will be set if not provided. + BaseURL string + // Mockable HTTP Client Interface + HTTPClient interface { + Do(*http.Request) (*http.Response, error) + } +} + +func (client *Client) do(request *http.Request) (*http.Response, error) { + if client.HTTPClient != nil { + return client.HTTPClient.Do(request) + } + return http.DefaultClient.Do(request) +} + +// SetupSharingProfile creates a user profile in the sandbox instance +func (client *Client) SetupSharingProfile(tokenRequest TokenRequest) (token string, err error) { + if client.BaseURL == "" { + if value, exists := os.LookupEnv("YOTI_API_URL"); exists && value != "" { + client.BaseURL = value + } else { + client.BaseURL = "https://api.yoti.com/sandbox/v1" + } + } + + requestEndpoint := "/apps/" + client.ClientSdkID + "/tokens" + requestBody, err := json.Marshal(tokenRequest) + if err != nil { + return + } + + request, err := (&yotirequest.SignedRequest{ + Key: client.Key, + HTTPMethod: http.MethodPost, + BaseURL: client.BaseURL, + Endpoint: requestEndpoint, + Headers: yotirequest.JSONHeaders(), + Body: requestBody, + }).Request() + if err != nil { + return + } + + response, err := client.do(request) + if err != nil { + return + } + if response.StatusCode != http.StatusCreated { + body, _ := io.ReadAll(response.Body) + return "", fmt.Errorf("Sharing Profile not created (HTTP %d) %s", response.StatusCode, string(body)) + } + + responseStruct := struct { + Token string `json:"token"` + }{} + + err = json.NewDecoder(response.Body).Decode(&responseStruct) + token = responseStruct.Token + + return +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client_test.go new file mode 100644 index 0000000..0507083 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/client_test.go @@ -0,0 +1,145 @@ +package sandbox + +import ( + "crypto/rand" + "crypto/rsa" + "io" + "net/http" + "os" + "strings" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "gotest.tools/v3/assert" +) + +func TestClient_SetupSharingProfile_ShouldReturnErrorIfProfileNotCreated(t *testing.T) { + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + BaseURL: "example.com", + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 401, + Body: io.NopCloser(strings.NewReader("")), + }, nil + }, + }, + } + _, err = client.SetupSharingProfile(TokenRequest{}) + assert.ErrorContains(t, err, "Sharing Profile not created") +} + +func TestClient_SetupSharingProfile_Success(t *testing.T) { + expectedToken := "shareToken" + key, err := rsa.GenerateKey(rand.Reader, 1024) + assert.NilError(t, err) + + client := Client{ + Key: key, + BaseURL: "example.com", + HTTPClient: &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"token":"` + expectedToken + `"}`)), + }, nil + }, + }, + } + token, err := client.SetupSharingProfile(TokenRequest{}) + assert.NilError(t, err) + + assert.Equal(t, token, expectedToken) +} +func TestClient_SetupSharingProfileUsesConstructorBaseUrlOverEnvVariable(t *testing.T) { + client := createSandboxClient(t, "constuctorBaseUrl") + os.Setenv("YOTI_API_URL", "envBaseUrl") + + _, err := client.SetupSharingProfile(TokenRequest{}) + assert.NilError(t, err) + + assert.Equal(t, "constuctorBaseUrl", client.BaseURL) +} + +func TestClient_SetupSharingProfileUsesEnvVariable(t *testing.T) { + client := createSandboxClient(t, "") + + os.Setenv("YOTI_API_URL", "envBaseUrl") + + _, err := client.SetupSharingProfile(TokenRequest{}) + assert.NilError(t, err) + + assert.Equal(t, "envBaseUrl", client.BaseURL) +} + +func TestClient_SetupSharingProfileUsesDefaultUrlAsFallbackWithEmptyEnvValue(t *testing.T) { + os.Setenv("YOTI_API_URL", "") + + client := createSandboxClient(t, "") + + _, err := client.SetupSharingProfile(TokenRequest{}) + assert.NilError(t, err) + + assert.Equal(t, "https://api.yoti.com/sandbox/v1", client.BaseURL) +} + +func TestClient_SetupSharingProfileUsesDefaultUrlAsFallbackWithNoEnvValue(t *testing.T) { + os.Unsetenv("YOTI_API_URL") + + client := createSandboxClient(t, "") + + _, err := client.SetupSharingProfile(TokenRequest{}) + assert.NilError(t, err) + + assert.Equal(t, "https://api.yoti.com/sandbox/v1", client.BaseURL) +} + +func createSandboxClient(t *testing.T, constructorBaseUrl string) (client Client) { + keyBytes, fileErr := os.ReadFile("../../test/test-key.pem") + assert.NilError(t, fileErr) + + pemFile, parseErr := cryptoutil.ParseRSAKey(keyBytes) + assert.NilError(t, parseErr) + + if constructorBaseUrl == "" { + return Client{ + Key: pemFile, + ClientSdkID: "ClientSDKID", + HTTPClient: mockHttpClientCreatedResponse(), + } + } + + return Client{ + Key: pemFile, + BaseURL: constructorBaseUrl, + ClientSdkID: "ClientSDKID", + HTTPClient: mockHttpClientCreatedResponse(), + } + +} + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func mockHttpClientCreatedResponse() *mockHTTPClient { + return &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 201, + Body: io.NopCloser(strings.NewReader(`{"token":"tokenValue"}`)), + }, nil + }, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images.go new file mode 100644 index 0000000..b331d05 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images.go @@ -0,0 +1,40 @@ +package sandbox + +import ( + "strings" + + "github.com/getyoti/yoti-go-sdk/v3/media" +) + +// DocumentImages describes a Document Images attribute on a sandbox profile +type DocumentImages struct { + Images []media.Media +} + +func (d DocumentImages) getValue() string { + var imageUrls []string + + for _, i := range d.Images { + imageUrls = append(imageUrls, i.Base64URL()) + } + + return strings.Join(imageUrls, "&") +} + +// WithPngImage adds a PNG image to the slice of document images +func (d DocumentImages) WithPngImage(imageContent []byte) DocumentImages { + pngImage := media.PNGImage(imageContent) + + d.Images = append(d.Images, pngImage) + + return d +} + +// WithJpegImage adds a JPEG image to the slice of document images +func (d DocumentImages) WithJpegImage(imageContent []byte) DocumentImages { + jpegImage := media.JPEGImage(imageContent) + + d.Images = append(d.Images, jpegImage) + + return d +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images_test.go new file mode 100644 index 0000000..baa3853 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/document_images_test.go @@ -0,0 +1,47 @@ +package sandbox + +import ( + "fmt" + "testing" + + "github.com/getyoti/yoti-go-sdk/v3/media" + "gotest.tools/v3/assert" +) + +const expectedBase64Content = "3q2+7w==" + +func TestShouldAddJpegImage(t *testing.T) { + documentImages := DocumentImages{}.WithJpegImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}) + + documentImage := documentImages.Images[0] + assert.Equal(t, media.ImageTypeJPEG, documentImage.MIME()) + assert.Equal( + t, + documentImages.getValue(), + fmt.Sprintf("data:image/jpeg;base64,%s", expectedBase64Content)) +} + +func TestShouldAddPngImage(t *testing.T) { + documentImages := DocumentImages{}.WithPngImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}) + + documentImage := documentImages.Images[0] + assert.Equal(t, media.ImageTypePNG, documentImage.MIME()) + assert.Equal( + t, + documentImages.getValue(), + fmt.Sprintf("data:image/png;base64,%s", expectedBase64Content)) +} + +func TestShouldAddMultipleImage(t *testing.T) { + documentImages := DocumentImages{}. + WithPngImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}). + WithPngImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}). + WithJpegImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}) + + assert.Equal(t, 3, len(documentImages.Images)) + + assert.Equal( + t, + documentImages.getValue(), + fmt.Sprintf("data:image/png;base64,%[1]s&data:image/png;base64,%[1]s&data:image/jpeg;base64,%[1]s", expectedBase64Content)) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest.go new file mode 100644 index 0000000..5010c82 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest.go @@ -0,0 +1,133 @@ +package sandbox + +import ( + "encoding/base64" + "encoding/json" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/consts" +) + +// TokenRequest describes a sandbox token request +type TokenRequest struct { + RememberMeID string `json:"remember_me_id"` + Attributes []Attribute `json:"profile_attributes"` +} + +// WithRememberMeID adds the Remember Me ID to the returned ActivityDetails. +// The value returned in ActivityDetails will be the Base64 encoded value of the string specified here. +func (t TokenRequest) WithRememberMeID(rememberMeId string) TokenRequest { + t.RememberMeID = rememberMeId + return t +} + +// WithAttribute adds a new attribute to the sandbox token request +func (t TokenRequest) WithAttribute(name, value string, anchors []Anchor) TokenRequest { + if anchors == nil { + anchors = make([]Anchor, 0) + } + attribute := Attribute{ + Name: name, + Value: value, + Anchors: anchors, + } + + return t.WithAttributeStruct(attribute) +} + +// WithAttributeStruct adds a new attribute struct to the sandbox token request +func (t TokenRequest) WithAttributeStruct(attribute Attribute) TokenRequest { + t.Attributes = append(t.Attributes, attribute) + return t +} + +// WithGivenNames adds given names to the sandbox token request +func (t TokenRequest) WithGivenNames(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrGivenNames, value, anchors) +} + +// WithFamilyName adds a family name to the sandbox token request +func (t TokenRequest) WithFamilyName(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrFamilyName, value, anchors) +} + +// WithFullName adds a full name to the sandbox token request +func (t TokenRequest) WithFullName(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrFullName, value, anchors) +} + +// WithDateOfBirth adds a date of birth to the sandbox token request +func (t TokenRequest) WithDateOfBirth(value time.Time, anchors []Anchor) TokenRequest { + formattedTime := value.Format("2006-01-02") + return t.WithAttribute(consts.AttrDateOfBirth, formattedTime, anchors) +} + +// WithAgeVerification adds an age-based derivation attribute to the sandbox token request +func (t TokenRequest) WithAgeVerification(dateOfBirth time.Time, derivation Derivation, anchors []Anchor) TokenRequest { + if anchors == nil { + anchors = []Anchor{} + } + attribute := Attribute{ + Name: consts.AttrDateOfBirth, + Value: dateOfBirth.Format("2006-01-02"), + Derivation: derivation.ToString(), + Anchors: anchors, + } + t.Attributes = append(t.Attributes, attribute) + return t +} + +// WithGender adds a gender to the sandbox token request +func (t TokenRequest) WithGender(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrGender, value, anchors) +} + +// WithPhoneNumber adds a phone number to the sandbox token request +func (t TokenRequest) WithPhoneNumber(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrMobileNumber, value, anchors) +} + +// WithNationality adds a nationality to the sandbox token request +func (t TokenRequest) WithNationality(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrNationality, value, anchors) +} + +// WithPostalAddress adds a formatted address to the sandbox token request +func (t TokenRequest) WithPostalAddress(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrAddress, value, anchors) +} + +// WithStructuredPostalAddress adds a JSON address to the sandbox token request +func (t TokenRequest) WithStructuredPostalAddress(value map[string]interface{}, anchors []Anchor) TokenRequest { + data, _ := json.Marshal(value) + return t.WithAttribute(consts.AttrStructuredPostalAddress, string(data), anchors) +} + +// WithSelfie adds a selfie image to the sandbox token request +func (t TokenRequest) WithSelfie(value []byte, anchors []Anchor) TokenRequest { + return t.WithBase64Selfie(base64.StdEncoding.EncodeToString(value), anchors) +} + +// WithBase64Selfie adds a base 64 selfie image to the sandbox token request +func (t TokenRequest) WithBase64Selfie(base64Value string, anchors []Anchor) TokenRequest { + return t.WithAttribute( + consts.AttrSelfie, + base64Value, + anchors, + ) +} + +// WithEmailAddress adds an email address to the sandbox token request +func (t TokenRequest) WithEmailAddress(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrEmailAddress, value, anchors) +} + +// WithDocumentDetails adds a document details string to the sandbox token request +func (t TokenRequest) WithDocumentDetails(value string, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrDocumentDetails, value, anchors) +} + +// WithDocumentImages adds document images to the sandbox token request +func (t TokenRequest) WithDocumentImages(value DocumentImages, anchors []Anchor) TokenRequest { + return t.WithAttribute(consts.AttrDocumentImages, value.getValue(), anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest_test.go new file mode 100644 index 0000000..b789133 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/sandbox/tokenrequest_test.go @@ -0,0 +1,198 @@ +package sandbox + +import ( + "encoding/json" + "fmt" + "time" +) + +func AnchorList() []Anchor { + return []Anchor{ + SourceAnchor("", time.Unix(1234567890, 0), ""), + VerifierAnchor("", time.Unix(1234567890, 0), ""), + } +} + +func ExampleTokenRequest_WithRememberMeID() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithRememberMeID("some-remember-me-id") + + printJson(tokenRequest) + // Output: {"remember_me_id":"some-remember-me-id","profile_attributes":null} +} + +func ExampleTokenRequest_WithAttribute() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithAttribute( + "AttributeName1", + "Value", + AnchorList(), + ).WithAttribute( + "AttributeName2", + "Value", + nil, + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"AttributeName1","value":"Value","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]},{"name":"AttributeName2","value":"Value","derivation":"","optional":"","anchors":[]}]} +} + +func ExampleTokenRequest_WithAttributeStruct() { + attribute := Attribute{ + Name: "AttributeName3", + Value: "Value3", + Anchors: AnchorList(), + } + + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithAttributeStruct(attribute) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"AttributeName3","value":"Value3","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithGivenNames() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithGivenNames( + "Value", + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"given_names","value":"Value","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithFamilyName() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithFamilyName( + "Value", + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"family_name","value":"Value","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithFullName() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithFullName( + "Value", + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"full_name","value":"Value","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithDateOfBirth() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithDateOfBirth(time.Unix(1234567890, 0), AnchorList()) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"date_of_birth","value":"2009-02-13","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithAgeVerification() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithAgeVerification( + time.Unix(1234567890, 0), + Derivation{}.AgeOver(18), + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"date_of_birth","value":"2009-02-13","derivation":"age_over:18","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithGender() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithGender("male", AnchorList()) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"gender","value":"male","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithPhoneNumber() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithPhoneNumber("00005550000", AnchorList()) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"phone_number","value":"00005550000","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithNationality() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithNationality("Value", AnchorList()) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"nationality","value":"Value","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithPostalAddress() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithPostalAddress("Value", AnchorList()) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"postal_address","value":"Value","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithStructuredPostalAddress() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithStructuredPostalAddress( + map[string]interface{}{ + "FormattedAddressLine": "Value", + }, + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"structured_postal_address","value":"{\"FormattedAddressLine\":\"Value\"}","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithSelfie() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithSelfie( + []byte{0xDE, 0xAD, 0xBE, 0xEF}, + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"selfie","value":"3q2+7w==","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithBase64Selfie() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithBase64Selfie( + "3q2+7w==", + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"selfie","value":"3q2+7w==","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithEmailAddress() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithEmailAddress("user@example.com", AnchorList()) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"email_address","value":"user@example.com","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithDocumentDetails() { + time.Local = time.UTC + tokenRequest := TokenRequest{}.WithDocumentDetails( + "DRIVING_LICENCE - abc1234", + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"document_details","value":"DRIVING_LICENCE - abc1234","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func ExampleTokenRequest_WithDocumentImages() { + time.Local = time.UTC + + documentImages := DocumentImages{}.WithPngImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}).WithJpegImage([]byte{0xDE, 0xAD, 0xBE, 0xEF}) + + tokenRequest := TokenRequest{}.WithDocumentImages( + documentImages, + AnchorList(), + ) + printJson(tokenRequest) + // Output: {"remember_me_id":"","profile_attributes":[{"name":"document_images","value":"data:image/png;base64,3q2+7w==\u0026data:image/jpeg;base64,3q2+7w==","derivation":"","optional":"","anchors":[{"type":"SOURCE","value":"","sub_type":"","timestamp":1234567890000},{"type":"VERIFIER","value":"","sub_type":"","timestamp":1234567890000}]}]} +} + +func printJson(value interface{}) { + marshalledJSON, err := json.Marshal(value) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(marshalledJSON)) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service.go new file mode 100644 index 0000000..3f9ae50 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service.go @@ -0,0 +1,145 @@ +package profile + +import ( + "crypto/rsa" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strconv" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/extra" + "github.com/getyoti/yoti-go-sdk/v3/requests" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +func getProfileEndpoint(token, sdkID string) string { + return fmt.Sprintf("/profile/%s?appId=%s", token, sdkID) +} + +// GetActivityDetails requests information about a Yoti user using the one time +// use token generated by the Yoti login process. Don't call this directly, use yoti.GetActivityDetails +func GetActivityDetails(httpClient requests.HttpClient, token, clientSdkId, apiUrl string, key *rsa.PrivateKey) (activity ActivityDetails, err error) { + if len(token) < 1 { + return activity, yotierror.InvalidTokenError + } + + var decryptedToken string + decryptedToken, err = cryptoutil.DecryptToken(token, key) + if err != nil { + return activity, yotierror.TokenDecryptError + } + + headers := requests.AuthKeyHeader(&key.PublicKey) + + request, err := requests.SignedRequest{ + Key: key, + HTTPMethod: http.MethodGet, + BaseURL: apiUrl, + Endpoint: getProfileEndpoint(decryptedToken, clientSdkId), + Headers: headers, + Body: nil, + }.Request() + if err != nil { + return + } + + response, err := requests.Execute(httpClient, request, map[int]string{404: "Profile not found"}, yotierror.DefaultHTTPErrorMessages) + if err != nil { + return activity, err + } + + responseBytes, err := io.ReadAll(response.Body) + if err != nil { + return + } + + return handleSuccessfulResponse(responseBytes, key) +} + +func handleSuccessfulResponse(responseBytes []byte, key *rsa.PrivateKey) (activityDetails ActivityDetails, err error) { + var parsedResponse = profileDO{} + + if err = json.Unmarshal(responseBytes, &parsedResponse); err != nil { + return + } + + if parsedResponse.Receipt.SharingOutcome != "SUCCESS" { + return activityDetails, handleUnsuccessfulShare(parsedResponse) + } + + var userAttributeList, applicationAttributeList *yotiprotoattr.AttributeList + if userAttributeList, err = parseUserProfile(&parsedResponse.Receipt, key); err != nil { + return + } + if applicationAttributeList, err = parseApplicationProfile(&parsedResponse.Receipt, key); err != nil { + return + } + id := parsedResponse.Receipt.RememberMeID + + userProfile := newUserProfile(userAttributeList) + applicationProfile := newApplicationProfile(applicationAttributeList) + + var extraData *extra.Data + extraData, err = parseExtraData(&parsedResponse.Receipt, key, err) + + timestamp, timestampErr := time.Parse(time.RFC3339Nano, parsedResponse.Receipt.Timestamp) + if timestampErr != nil { + err = yotierror.MultiError{This: errors.New("Unable to read timestamp. Error: " + timestampErr.Error()), Next: err} + } + + activityDetails = ActivityDetails{ + UserProfile: userProfile, + rememberMeID: id, + parentRememberMeID: parsedResponse.Receipt.ParentRememberMeID, + timestamp: timestamp, + receiptID: parsedResponse.Receipt.ReceiptID, + ApplicationProfile: applicationProfile, + extraData: extraData, + } + + return activityDetails, err +} + +func parseExtraData(receipt *receiptDO, key *rsa.PrivateKey, err error) (*extra.Data, error) { + decryptedExtraData, decryptErr := decryptExtraData(receipt, key) + if decryptErr != nil { + err = yotierror.MultiError{This: errors.New("Unable to decrypt ExtraData from the receipt. Error: " + decryptErr.Error()), Next: err} + } + + extraData, parseErr := extra.NewExtraData(decryptedExtraData) + if parseErr != nil { + err = yotierror.MultiError{This: errors.New("Unable to parse ExtraData from the receipt. Error: " + parseErr.Error()), Next: err} + } + return extraData, err +} + +func parseIsAgeVerifiedValue(byteValue []byte) (result *bool, err error) { + stringValue := string(byteValue) + + var parseResult bool + parseResult, err = strconv.ParseBool(stringValue) + + if err != nil { + return nil, err + } + + result = &parseResult + + return +} + +func handleUnsuccessfulShare(parsedResponse profileDO) error { + if parsedResponse.ErrorDetails != nil && parsedResponse.ErrorDetails.ErrorCode != nil { + return yotierror.DetailedSharingFailureError{ + Code: parsedResponse.ErrorDetails.ErrorCode, + Description: parsedResponse.ErrorDetails.Description, + } + } + + return yotierror.SharingFailureError +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service_test.go new file mode 100644 index 0000000..cc07c0f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/service_test.go @@ -0,0 +1,465 @@ +package profile + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/rsa" + "encoding/base64" + "io" + "net/http" + "net/url" + "os" + "strings" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" + "github.com/getyoti/yoti-go-sdk/v3/yotierror" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotocom" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" + + "github.com/getyoti/yoti-go-sdk/v3/test" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func TestProfileService_ParseIsAgeVerifiedValue_True(t *testing.T) { + trueValue := []byte("true") + + isAgeVerified, err := parseIsAgeVerifiedValue(trueValue) + assert.NilError(t, err, "Failed to parse IsAgeVerified value") + assert.Check(t, *isAgeVerified) +} + +func TestProfileService_ParseIsAgeVerifiedValue_False(t *testing.T) { + falseValue := []byte("false") + + isAgeVerified, err := parseIsAgeVerifiedValue(falseValue) + assert.NilError(t, err, "Failed to parse IsAgeVerified value") + assert.Check(t, !*isAgeVerified) + +} +func TestProfileService_ParseIsAgeVerifiedValue_InvalidValueThrowsError(t *testing.T) { + invalidValue := []byte("invalidBool") + + _, err := parseIsAgeVerifiedValue(invalidValue) + + assert.Assert(t, err != nil) +} + +func TestProfileService_ErrIsThrownForInvalidToken(t *testing.T) { + _, err := GetActivityDetails(nil, "invalidToken", "clientSdkId", "apiUrl", getValidKey()) + + assert.ErrorContains(t, err, "unable to decrypt token") +} + +func TestProfileService_RequestErrIsReturned(t *testing.T) { + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 404, + }, nil + }, + } + _, err := GetActivityDetails(client, test.EncryptedToken, "clientSdkId", "https://apiUrl", getValidKey()) + + assert.ErrorContains(t, err, "404: Profile not found") +} + +func TestProfileService_InvalidToken(t *testing.T) { + _, err := GetActivityDetails(nil, "", "sdkId", "https://apiurl", getValidKey()) + assert.ErrorContains(t, err, "invalid token") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestProfileService_ParseExtraData_ErrorDecrypting(t *testing.T) { + receipt := &receiptDO{ + ExtraDataContent: "invalidExtraData", + } + _, err := parseExtraData(receipt, getValidKey(), nil) + + assert.ErrorContains(t, err, "Unable to decrypt ExtraData from the receipt.") +} + +func TestProfileService_GetActivityDetails(t *testing.T) { + key := getValidKey() + + otherPartyProfileContent := "ChCZAib1TBm9Q5GYfFrS1ep9EnAwQB5shpAPWLBgZgFgt6bCG3S5qmZHhrqUbQr3yL6yeLIDwbM7x4nuT/MYp+LDXgmFTLQNYbDTzrEzqNuO2ZPn9Kpg+xpbm9XtP7ZLw3Ep2BCmSqtnll/OdxAqLb4DTN4/wWdrjnFC+L/oQEECu646" + rememberMeID := "remember_me_id0123456789" + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"receipt":{"wrapped_receipt_key": "` + test.WrappedReceiptKey + `","other_party_profile_content": "` + otherPartyProfileContent + `","remember_me_id":"` + rememberMeID + `", "sharing_outcome":"SUCCESS", "timestamp":"2006-01-02T15:04:05.999999Z"}}`)), + }, nil + }, + } + + activityDetails, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + assert.NilError(t, err) + + profile := activityDetails.UserProfile + + assert.Equal(t, activityDetails.RememberMeID(), rememberMeID) + assert.Assert(t, is.Nil(activityDetails.ExtraData().AttributeIssuanceDetails())) + + expectedSelfieValue := "selfie0123456789" + + assert.DeepEqual(t, profile.Selfie().Value().Data(), []byte(expectedSelfieValue)) + assert.Equal(t, profile.MobileNumber().Value(), "phone_number0123456789") + + assert.Equal( + t, + profile.GetAttribute("phone_number").Value(), + "phone_number0123456789", + ) + + assert.Check(t, + profile.GetImageAttribute("doesnt_exist") == nil, + ) + + assert.Check(t, profile.GivenNames() == nil) + assert.Check(t, profile.FamilyName() == nil) + assert.Check(t, profile.FullName() == nil) + assert.Check(t, profile.EmailAddress() == nil) + images, err := profile.DocumentImages() + assert.NilError(t, err) + assert.Check(t, images == nil) + documentDetails, err := profile.DocumentDetails() + assert.NilError(t, err) + assert.Check(t, documentDetails == nil) + + expectedDoB := time.Date(1980, time.January, 1, 0, 0, 0, 0, time.UTC) + + actualDoB, err := profile.DateOfBirth() + assert.NilError(t, err) + + assert.Assert(t, actualDoB != nil) + assert.DeepEqual(t, actualDoB.Value(), &expectedDoB) +} + +func TestProfileService_SharingFailure_ReturnsSpecificFailure(t *testing.T) { + key := getValidKey() + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"session_data":"session_data","receipt":{"receipt_id": null,"other_party_profile_content": null,"policy_uri":null,"personal_key":null,"remember_me_id":null, "sharing_outcome":"FAILURE","timestamp":"2016-09-23T13:04:11Z"},"error_details":{"error_code":"SOME_ERROR","description":"SOME_DESCRIPTION"}}`)), + }, nil + }, + } + + errorCode := "SOME_ERROR" + + description := "SOME_DESCRIPTION" + + expectedError := yotierror.DetailedSharingFailureError{ + Code: &errorCode, + Description: &description, + } + + _, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + + assert.DeepEqual(t, err, expectedError) + + assert.ErrorContains(t, err, "sharing failure") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestProfileService_SharingFailure_ReturnsGenericErrorWhenErrorCodeIsNull(t *testing.T) { + key := getValidKey() + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"session_data":"session_data","receipt":{"receipt_id": null,"other_party_profile_content": null,"policy_uri":null,"personal_key":null,"remember_me_id":null, "sharing_outcome":"FAILURE","timestamp":"2016-09-23T13:04:11Z"},"error_details":{}}`)), + }, nil + }, + } + + expectedError := yotierror.DetailedSharingFailureError{ + Code: nil, + Description: nil, + } + + _, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + + assert.DeepEqual(t, err, expectedError) + + assert.ErrorContains(t, err, "sharing failure") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestProfileService_SharingFailure_ReturnsGenericFailure(t *testing.T) { + key := getValidKey() + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"session_data":"session_data","receipt":{"receipt_id": null,"other_party_profile_content": null,"policy_uri":null,"personal_key":null,"remember_me_id":null, "sharing_outcome":"FAILURE","timestamp":"2016-09-23T13:04:11Z"}}`)), + }, nil + }, + } + _, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + + assert.ErrorContains(t, err, "sharing failure") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, !temporary || !tempError.Temporary()) +} + +func TestProfileService_TokenDecodedSuccessfully(t *testing.T) { + key := getValidKey() + + expectedPath := "/profile/" + test.Token + + client := &mockHTTPClient{ + do: func(request *http.Request) (*http.Response, error) { + parsed, err := url.Parse(request.URL.String()) + assert.NilError(t, err, "Yoti API did not generate a valid URI.") + assert.Equal(t, parsed.Path, expectedPath, "Yoti API did not generate a valid URL path.") + + return &http.Response{ + StatusCode: 500, + }, nil + }, + } + + _, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + assert.ErrorContains(t, err, "unknown HTTP error") + + tempError, temporary := err.(interface { + Temporary() bool + }) + assert.Check(t, temporary && tempError.Temporary()) +} + +func TestProfileService_ParentRememberMeID(t *testing.T) { + key := getValidKey() + + otherPartyProfileContent := "ChCZAib1TBm9Q5GYfFrS1ep9EnAwQB5shpAPWLBgZgFgt6bCG3S5qmZHhrqUbQr3yL6yeLIDwbM7x4nuT/MYp+LDXgmFTLQNYbDTzrEzqNuO2ZPn9Kpg+xpbm9XtP7ZLw3Ep2BCmSqtnll/OdxAqLb4DTN4/wWdrjnFC+L/oQEECu646" + parentRememberMeID := "parent_remember_me_id0123456789" + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"receipt":{"wrapped_receipt_key": "` + test.WrappedReceiptKey + + `","other_party_profile_content": "` + otherPartyProfileContent + + `","parent_remember_me_id":"` + parentRememberMeID + + `", "sharing_outcome":"SUCCESS", "timestamp":"2006-01-02T15:04:05.999999Z"}}`)), + }, nil + }, + } + + activityDetails, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + + assert.NilError(t, err) + assert.Equal(t, activityDetails.ParentRememberMeID(), parentRememberMeID) +} +func TestProfileService_ParseWithoutProfile_Success(t *testing.T) { + key := getValidKey() + + rememberMeID := "remember_me_id0123456789" + timestamp := time.Date(1973, 11, 29, 9, 33, 9, 0, time.UTC) + timestampString := func(a []byte, _ error) string { + return string(a) + }(timestamp.MarshalText()) + receiptID := "receipt_id123" + + var otherPartyProfileContents = []string{ + `"other_party_profile_content": null,`, + `"other_party_profile_content": "",`, + ``} + + for _, otherPartyProfileContent := range otherPartyProfileContents { + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"receipt":{"wrapped_receipt_key": "` + test.WrappedReceiptKey + `",` + + otherPartyProfileContent + `"remember_me_id":"` + rememberMeID + `", "sharing_outcome":"SUCCESS", "timestamp":"` + timestampString + `", "receipt_id":"` + receiptID + `"}}`)), + }, nil + }, + } + + activityDetails, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + + assert.NilError(t, err) + assert.Equal(t, activityDetails.RememberMeID(), rememberMeID) + assert.Equal(t, activityDetails.Timestamp(), timestamp) + assert.Equal(t, activityDetails.ReceiptID(), receiptID) + } +} + +func TestProfileService_ShouldParseAndDecryptExtraDataContent(t *testing.T) { + otherPartyProfileContent := "ChCZAib1TBm9Q5GYfFrS1ep9EnAwQB5shpAPWLBgZgFgt6bCG3S5qmZHhrqUbQr3yL6yeLIDwbM7x4nuT/MYp+LDXgmFTLQNYbDTzrEzqNuO2ZPn9Kpg+xpbm9XtP7ZLw3Ep2BCmSqtnll/OdxAqLb4DTN4/wWdrjnFC+L/oQEECu646" + rememberMeID := "remember_me_id0123456789" + + pemBytes, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + dataEntries := make([]*yotiprotoshare.DataEntry, 0) + expiryDate := time.Now().UTC().AddDate(0, 0, 1) + thirdPartyAttributeDataEntry := test.CreateThirdPartyAttributeDataEntry(t, &expiryDate, []string{attributeName}, "tokenValue") + + dataEntries = append(dataEntries, &thirdPartyAttributeDataEntry) + protoExtraData := &yotiprotoshare.ExtraData{ + List: dataEntries, + } + + extraDataContent := createExtraDataContent(t, protoExtraData) + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"receipt":{"wrapped_receipt_key": "` + + test.WrappedReceiptKey + `","other_party_profile_content": "` + otherPartyProfileContent + `","extra_data_content": "` + + extraDataContent + `","remember_me_id":"` + rememberMeID + `", "sharing_outcome":"SUCCESS", "timestamp":"2006-01-02T15:04:05.999999Z"}}`)), + }, nil + }, + } + key, err := cryptoutil.ParseRSAKey(pemBytes) + assert.NilError(t, err) + + activityDetails, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + assert.NilError(t, err) + + assert.Equal(t, rememberMeID, activityDetails.RememberMeID()) + assert.Assert(t, activityDetails.ExtraData().AttributeIssuanceDetails() != nil) + assert.Equal(t, activityDetails.UserProfile.MobileNumber().Value(), "phone_number0123456789") +} + +func TestProfileService_ShouldCarryOnProcessingIfIssuanceTokenIsNotPresent(t *testing.T) { + dataEntries := make([]*yotiprotoshare.DataEntry, 0) + expiryDate := time.Now().UTC().AddDate(0, 0, 1) + thirdPartyAttributeDataEntry := test.CreateThirdPartyAttributeDataEntry(t, &expiryDate, []string{attributeName}, "") + + dataEntries = append(dataEntries, &thirdPartyAttributeDataEntry) + protoExtraData := &yotiprotoshare.ExtraData{ + List: dataEntries, + } + + pemBytes, err := os.ReadFile("../test/test-key.pem") + assert.NilError(t, err) + + extraDataContent := createExtraDataContent(t, protoExtraData) + + otherPartyProfileContent := "ChCZAib1TBm9Q5GYfFrS1ep9EnAwQB5shpAPWLBgZgFgt6bCG3S5qmZHhrqUbQr3yL6yeLIDwbM7x4nuT/MYp+LDXgmFTLQNYbDTzrEzqNuO2ZPn9Kpg+xpbm9XtP7ZLw3Ep2BCmSqtnll/OdxAqLb4DTN4/wWdrjnFC+L/oQEECu646" + + rememberMeID := "remember_me_id0123456789" + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"receipt":{"wrapped_receipt_key": "` + + test.WrappedReceiptKey + `","other_party_profile_content": "` + otherPartyProfileContent + `","extra_data_content": "` + + extraDataContent + `","remember_me_id":"` + rememberMeID + `", "sharing_outcome":"SUCCESS"}}`)), + }, nil + }, + } + + key, err := cryptoutil.ParseRSAKey(pemBytes) + assert.NilError(t, err) + + activityDetails, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", key) + + assert.Check(t, err != nil) + assert.Check(t, strings.Contains(err.Error(), "Issuance Token is invalid")) + + assert.Equal(t, rememberMeID, activityDetails.RememberMeID()) + assert.Assert(t, is.Nil(activityDetails.ExtraData().AttributeIssuanceDetails())) + assert.Equal(t, activityDetails.UserProfile.MobileNumber().Value(), "phone_number0123456789") +} +func TestProfileService_ParseWithoutRememberMeID_Success(t *testing.T) { + var otherPartyProfileContents = []string{ + `"other_party_profile_content": null,`, + `"other_party_profile_content": "",`} + + for _, otherPartyProfileContent := range otherPartyProfileContents { + + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(strings.NewReader(`{"receipt":{"wrapped_receipt_key": "` + test.WrappedReceiptKey + `",` + + otherPartyProfileContent + `"sharing_outcome":"SUCCESS", "timestamp":"2006-01-02T15:04:05.999999Z"}}`)), + }, nil + }, + } + _, err := GetActivityDetails(client, test.EncryptedToken, "sdkId", "https://apiurl", getValidKey()) + + assert.NilError(t, err) + } +} + +func getValidKey() *rsa.PrivateKey { + return test.GetValidKey("../test/test-key.pem") +} + +func createExtraDataContent(t *testing.T, protoExtraData *yotiprotoshare.ExtraData) string { + outBytes, err := proto.Marshal(protoExtraData) + assert.NilError(t, err) + + key := getValidKey() + assert.NilError(t, err) + + cipherBytes, err := base64.StdEncoding.DecodeString(test.WrappedReceiptKey) + assert.NilError(t, err) + unwrappedKey, err := rsa.DecryptPKCS1v15(rand.Reader, key, cipherBytes) + assert.NilError(t, err) + cipherBlock, err := aes.NewCipher(unwrappedKey) + assert.NilError(t, err) + + padLength := cipherBlock.BlockSize() - len(outBytes)%cipherBlock.BlockSize() + outBytes = append(outBytes, bytes.Repeat([]byte{byte(padLength)}, padLength)...) + + iv := make([]byte, cipherBlock.BlockSize()) + encrypter := cipher.NewCBCEncrypter(cipherBlock, iv) + encrypter.CryptBlocks(outBytes, outBytes) + + outProto := &yotiprotocom.EncryptedData{ + CipherText: outBytes, + Iv: iv, + } + outBytes, err = proto.Marshal(outProto) + assert.NilError(t, err) + + return base64.StdEncoding.EncodeToString(outBytes) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile.go new file mode 100644 index 0000000..3ab7648 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile.go @@ -0,0 +1,182 @@ +package profile + +import ( + "strings" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/profile/attribute" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" +) + +// UserProfile 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 UserProfile struct { + baseProfile +} + +// Creates a new Profile struct +func newUserProfile(attributes *yotiprotoattr.AttributeList) UserProfile { + return UserProfile{ + baseProfile{ + attributeSlice: createAttributeSlice(attributes), + }, + } +} + +func createAttributeSlice(protoAttributeList *yotiprotoattr.AttributeList) (result []*yotiprotoattr.Attribute) { + if protoAttributeList != nil { + result = append(result, protoAttributeList.Attributes...) + } + + return result +} + +// Selfie is a photograph of the user. Will be nil if not provided by Yoti. +func (p UserProfile) Selfie() *attribute.ImageAttribute { + return p.GetImageAttribute(consts.AttrSelfie) +} + +// GetSelfieAttributeByID retrieve a Selfie attribute by ID on the Yoti profile. +// This attribute is a photograph of the user. +// Will return nil if attribute is not present. +func (p UserProfile) GetSelfieAttributeByID(attributeID string) (*attribute.ImageAttribute, error) { + for _, a := range p.attributeSlice { + if a.EphemeralId == attributeID { + return attribute.NewImage(a) + } + } + return nil, nil +} + +// GivenNames corresponds to secondary names in passport, and first/middle names in English. Will be nil if not provided by Yoti. +func (p UserProfile) GivenNames() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrGivenNames) +} + +// FamilyName corresponds to primary name in passport, and surname in English. Will be nil if not provided by Yoti. +func (p UserProfile) FamilyName() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrFamilyName) +} + +// FullName represents the user's full name. +// If family_name/given_names are present, the value will be equal to the string 'given_names + " " family_name'. +// Will be nil if not provided by Yoti. +func (p UserProfile) FullName() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrFullName) +} + +// MobileNumber represents the user's mobile phone number, as verified at registration time. +// The value will be a number in E.164 format (i.e. '+' for international prefix and no spaces, e.g. "+447777123456"). +// Will be nil if not provided by Yoti. +func (p UserProfile) MobileNumber() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrMobileNumber) +} + +// EmailAddress represents the user's verified email address. Will be nil if not provided by Yoti. +func (p UserProfile) EmailAddress() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrEmailAddress) +} + +// 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 UserProfile) DateOfBirth() (*attribute.DateAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == consts.AttrDateOfBirth { + return attribute.NewDate(a) + } + } + return nil, nil +} + +// Address represents the user's address. Will be nil if not provided by Yoti. +func (p UserProfile) Address() *attribute.StringAttribute { + addressAttribute := p.GetStringAttribute(consts.AttrAddress) + if addressAttribute == nil { + return ensureAddressProfile(&p) + } + + return addressAttribute +} + +// StructuredPostalAddress represents the user's address in a JSON format. +// Will be nil if not provided by Yoti. This can be accessed as a +// map[string]string{} using a type assertion, e.g.: +// structuredPostalAddress := structuredPostalAddressAttribute.Value().(map[string]string{}) +func (p UserProfile) StructuredPostalAddress() (*attribute.JSONAttribute, error) { + return p.GetJSONAttribute(consts.AttrStructuredPostalAddress) +} + +// Gender corresponds to the gender in the registered document; the value will be one of the strings "MALE", "FEMALE", "TRANSGENDER" or "OTHER". +// Will be nil if not provided by Yoti. +func (p UserProfile) Gender() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrGender) +} + +// Nationality corresponds to the nationality in the passport. +// The value is an ISO-3166-1 alpha-3 code with ICAO9303 (passport) extensions. +// Will be nil if not provided by Yoti. +func (p UserProfile) Nationality() *attribute.StringAttribute { + return p.GetStringAttribute(consts.AttrNationality) +} + +// DocumentImages returns a slice of document images cropped from the image in the capture page. +// There can be multiple images as per the number of regions in the capture in this attribute. +// Will be nil if not provided by Yoti. +func (p UserProfile) DocumentImages() (*attribute.ImageSliceAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == consts.AttrDocumentImages { + return attribute.NewImageSlice(a) + } + } + return nil, nil +} + +// GetDocumentImagesAttributeByID retrieve a Document Images attribute by ID on the Yoti profile. +// This attribute consists of a slice of document images cropped from the image in the capture page. +// There can be multiple images as per the number of regions in the capture in this attribute. +// Will return nil if attribute is not present. +func (p UserProfile) GetDocumentImagesAttributeByID(attributeID string) (*attribute.ImageSliceAttribute, error) { + for _, a := range p.attributeSlice { + if a.EphemeralId == attributeID { + return attribute.NewImageSlice(a) + } + } + return nil, nil +} + +// DocumentDetails represents information extracted from a document provided by the user. +// Will be nil if not provided by Yoti. +func (p UserProfile) DocumentDetails() (*attribute.DocumentDetailsAttribute, error) { + for _, a := range p.attributeSlice { + if a.Name == consts.AttrDocumentDetails { + return attribute.NewDocumentDetails(a) + } + } + return nil, nil +} + +// IdentityProfileReport represents the JSON object containing identity assertion and the +// verification report. Will be nil if not provided by Yoti. +func (p UserProfile) IdentityProfileReport() (*attribute.JSONAttribute, error) { + return p.GetJSONAttribute(consts.AttrIdentityProfileReport) +} + +// AgeVerifications returns a slice of age verifications for the user. +// Will be an empty slice if not provided by Yoti. +func (p UserProfile) AgeVerifications() (out []attribute.AgeVerification, err error) { + ageUnderString := strings.Replace(consts.AttrAgeUnder, "%d", "", -1) + ageOverString := strings.Replace(consts.AttrAgeOver, "%d", "", -1) + + for _, a := range p.attributeSlice { + if strings.HasPrefix(a.Name, ageUnderString) || + strings.HasPrefix(a.Name, ageOverString) { + verification, err := attribute.NewAgeVerification(a) + if err != nil { + return nil, err + } + out = append(out, verification) + } + } + return out, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile_test.go new file mode 100644 index 0000000..0750fa6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/profile/user_profile_test.go @@ -0,0 +1,704 @@ +package profile + +import ( + "encoding/base64" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/consts" + "github.com/getyoti/yoti-go-sdk/v3/file" + "github.com/getyoti/yoti-go-sdk/v3/media" + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoattr" + "google.golang.org/protobuf/proto" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +const ( + attributeName = "test_attribute_name" + attributeValueString = "value" + + documentImagesAttributeID = "document-images-attribute-id-123" + selfieAttributeID = "selfie-attribute-id-123" + fullNameAttributeID = "full-name-id-123" +) + +var attributeValue = []byte(attributeValueString) + +func getUserProfile() UserProfile { + userProfile := createProfileWithMultipleAttributes( + createDocumentImagesAttribute(documentImagesAttributeID), + createSelfieAttribute(yotiprotoattr.ContentType_JPEG, selfieAttributeID), + createStringAttribute("full_name", []byte("John Smith"), []*yotiprotoattr.Anchor{}, fullNameAttributeID)) + + return userProfile +} + +func ExampleUserProfile_GetAttributeByID() { + userProfile := getUserProfile() + fullNameAttribute := userProfile.GetAttributeByID("full-name-id-123") + value := fullNameAttribute.Value().(string) + + fmt.Println(value) + // Output: John Smith +} + +func ExampleUserProfile_GetDocumentImagesAttributeByID() { + userProfile := getUserProfile() + documentImagesAttribute, err := userProfile.GetDocumentImagesAttributeByID("document-images-attribute-id-123") + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(*documentImagesAttribute.ID()) + // Output: document-images-attribute-id-123 +} + +func ExampleUserProfile_GetSelfieAttributeByID() { + userProfile := getUserProfile() + selfieAttribute, err := userProfile.GetSelfieAttributeByID("selfie-attribute-id-123") + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(*selfieAttribute.ID()) + // Output: selfie-attribute-id-123 +} + +func createProfileWithSingleAttribute(attr *yotiprotoattr.Attribute) UserProfile { + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, attr) + + return UserProfile{ + baseProfile{ + attributeSlice: attributeSlice, + }, + } +} + +func createAppProfileWithSingleAttribute(attr *yotiprotoattr.Attribute) ApplicationProfile { + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, attr) + + return ApplicationProfile{ + baseProfile{ + attributeSlice: attributeSlice, + }, + } +} + +func createProfileWithMultipleAttributes(list ...*yotiprotoattr.Attribute) UserProfile { + return UserProfile{ + baseProfile{ + attributeSlice: list, + }, + } +} + +func TestProfile_AgeVerifications(t *testing.T) { + ageOver14 := &yotiprotoattr.Attribute{ + Name: "age_over:14", + Value: []byte("true"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + ageUnder18 := &yotiprotoattr.Attribute{ + Name: "age_under:18", + Value: []byte("true"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + ageOver18 := &yotiprotoattr.Attribute{ + Name: "age_over:18", + Value: []byte("false"), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithMultipleAttributes(ageOver14, ageUnder18, ageOver18) + ageVerifications, err := profile.AgeVerifications() + + assert.NilError(t, err) + assert.Equal(t, len(ageVerifications), 3) + + assert.Equal(t, ageVerifications[0].Age, 14) + assert.Equal(t, ageVerifications[0].CheckType, "age_over") + assert.Equal(t, ageVerifications[0].Result, true) + + assert.Equal(t, ageVerifications[1].Age, 18) + assert.Equal(t, ageVerifications[1].CheckType, "age_under") + assert.Equal(t, ageVerifications[1].Result, true) + + assert.Equal(t, ageVerifications[2].Age, 18) + assert.Equal(t, ageVerifications[2].CheckType, "age_over") + assert.Equal(t, ageVerifications[2].Result, false) +} + +func TestProfile_GetAttribute_EmptyString(t *testing.T) { + emptyString := "" + attributeValue := []byte(emptyString) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, att.Name(), attributeName) + assert.Equal(t, att.Value().(string), emptyString) +} + +func TestProfile_GetApplicationAttribute(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createProfileWithSingleAttribute(attr) + applicationAttribute := appProfile.GetAttribute(attributeName) + assert.Equal(t, applicationAttribute.Name(), attributeName) +} + +func TestProfile_GetApplicationName(t *testing.T) { + attributeValue := "APPLICATION NAME" + var attr = &yotiprotoattr.Attribute{ + Name: "application_name", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, attributeValue, appProfile.ApplicationName().Value()) +} + +func TestProfile_GetApplicationURL(t *testing.T) { + attributeValue := "APPLICATION URL" + var attr = &yotiprotoattr.Attribute{ + Name: "application_url", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, attributeValue, appProfile.ApplicationURL().Value()) +} + +func TestProfile_GetApplicationLogo(t *testing.T) { + attributeValue := "APPLICATION LOGO" + var attr = &yotiprotoattr.Attribute{ + Name: "application_logo", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, 16, len(appProfile.ApplicationLogo().Value().Data())) +} + +func TestProfile_GetApplicationBGColor(t *testing.T) { + attributeValue := "BG VALUE" + var attr = &yotiprotoattr.Attribute{ + Name: "application_receipt_bgcolor", + Value: []byte(attributeValue), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + appProfile := createAppProfileWithSingleAttribute(attr) + assert.Equal(t, attributeValue, appProfile.ApplicationReceiptBgColor().Value()) +} + +func TestProfile_GetAttribute_Int(t *testing.T) { + intValues := [5]int{0, 1, 123, -10, -1} + + for _, integer := range intValues { + assertExpectedIntegerIsReturned(t, integer) + } +} + +func assertExpectedIntegerIsReturned(t *testing.T, intValue int) { + intAsString := strconv.Itoa(intValue) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: []byte(intAsString), + ContentType: yotiprotoattr.ContentType_INT, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, att.Value().(int), intValue) +} + +func TestProfile_GetAttribute_InvalidInt_ReturnsNil(t *testing.T) { + invalidIntValue := "1985-01-01" + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: []byte(invalidIntValue), + ContentType: yotiprotoattr.ContentType_INT, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + + att := result.GetAttribute(attributeName) + + assert.Assert(t, is.Nil(att)) +} + +func TestProfile_EmptyStringIsAllowed(t *testing.T) { + emptyString := "" + attrValue := []byte(emptyString) + + var attr = &yotiprotoattr.Attribute{ + Name: consts.AttrGender, + Value: attrValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(attr) + att := profile.Gender() + + assert.Equal(t, att.Value(), emptyString) +} + +func TestProfile_GetAttribute_Time(t *testing.T) { + dateStringValue := "1985-01-01" + expectedDate := time.Date(1985, time.January, 1, 0, 0, 0, 0, time.UTC) + + attributeValueTime := []byte(dateStringValue) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValueTime, + ContentType: yotiprotoattr.ContentType_DATE, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, expectedDate, att.Value().(*time.Time).UTC()) +} + +func TestProfile_GetAttribute_Jpeg(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_JPEG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(attr) + att := profile.GetAttribute(attributeName) + + expected := media.JPEGImage(attributeValue) + result := att.Value().(media.JPEGImage) + + assert.DeepEqual(t, expected, result) + assert.Equal(t, expected.Base64URL(), result.Base64URL()) +} + +func TestProfile_GetAttribute_Png(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_PNG, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(attr) + att := profile.GetAttribute(attributeName) + + expected := media.PNGImage(attributeValue) + result := att.Value().(media.PNGImage) + + assert.DeepEqual(t, expected, result) + assert.Equal(t, expected.Base64URL(), result.Base64URL()) +} + +func TestProfile_GetAttribute_Bool(t *testing.T) { + var initialBoolValue = true + attrValue := []byte(strconv.FormatBool(initialBoolValue)) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attrValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + boolValue, err := strconv.ParseBool(att.Value().(string)) + + assert.NilError(t, err) + assert.Equal(t, initialBoolValue, boolValue) +} + +func TestProfile_GetAttribute_JSON(t *testing.T) { + addressFormat := "2" + + var structuredAddressBytes = []byte(` + { + "address_format": "` + addressFormat + `", + "building": "House No.86-A" + }`) + + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: structuredAddressBytes, + ContentType: yotiprotoattr.ContentType_JSON, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + retrievedAttributeMap := att.Value().(map[string]interface{}) + actualAddressFormat := retrievedAttributeMap["address_format"] + + assert.Equal(t, actualAddressFormat, addressFormat) +} + +func TestProfile_GetAttribute_Undefined(t *testing.T) { + var attr = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att := result.GetAttribute(attributeName) + + assert.Equal(t, att.Name(), attributeName) + assert.Equal(t, att.Value().(string), attributeValueString) +} + +func TestProfile_GetAttribute_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result := userProfile.GetAttribute("attributeName") + + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_GetAttributeByID(t *testing.T) { + attributeID := "att-id-123" + + var attr1 = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + EphemeralId: attributeID, + } + var attr2 = &yotiprotoattr.Attribute{ + Name: attributeName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + EphemeralId: "non-matching-attribute-ID", + } + + profile := createProfileWithMultipleAttributes(attr1, attr2) + + result := profile.GetAttributeByID(attributeID) + assert.DeepEqual(t, result.ID(), &attributeID) +} + +func TestProfile_GetAttributeByID_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result := userProfile.GetAttributeByID("attributeName") + + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_GetDocumentImagesAttributeByID_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result, err := userProfile.GetDocumentImagesAttributeByID("attributeName") + assert.NilError(t, err) + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_GetSelfieAttributeByID_ReturnsNil(t *testing.T) { + userProfile := UserProfile{ + baseProfile{ + attributeSlice: []*yotiprotoattr.Attribute{}, + }, + } + + result, err := userProfile.GetSelfieAttributeByID("attributeName") + assert.NilError(t, err) + assert.Assert(t, is.Nil(result)) +} + +func TestProfile_StringAttribute(t *testing.T) { + nationalityName := consts.AttrNationality + + var as = &yotiprotoattr.Attribute{ + Name: nationalityName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(as) + + assert.Equal(t, result.Nationality().Value(), attributeValueString) + + assert.Equal(t, result.Nationality().ContentType(), yotiprotoattr.ContentType_STRING.String()) +} + +func TestProfile_AttributeProperty_RetrievesAttribute(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.Equal(t, selfie.Name(), consts.AttrSelfie) + assert.DeepEqual(t, attributeValue, selfie.Value().Data()) + assert.Equal(t, selfie.ContentType(), yotiprotoattr.ContentType_PNG.String()) +} + +func TestProfile_DocumentDetails_RetrievesAttribute(t *testing.T) { + documentDetailsName := consts.AttrDocumentDetails + attributeValue := []byte("PASSPORT GBR 1234567") + + var protoAttribute = &yotiprotoattr.Attribute{ + Name: documentDetailsName, + Value: attributeValue, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: make([]*yotiprotoattr.Anchor, 0), + } + + result := createProfileWithSingleAttribute(protoAttribute) + documentDetails, err := result.DocumentDetails() + assert.NilError(t, err) + + assert.Equal(t, documentDetails.Value().DocumentType, "PASSPORT") +} + +func TestProfile_DocumentImages_RetrievesAttribute(t *testing.T) { + protoAttribute := createDocumentImagesAttribute("attr-id") + + result := createProfileWithSingleAttribute(protoAttribute) + documentImages, err := result.DocumentImages() + assert.NilError(t, err) + + assert.Equal(t, documentImages.Name(), consts.AttrDocumentImages) +} + +func TestProfile_AttributesReturnsNilWhenNotPresent(t *testing.T) { + documentImagesName := consts.AttrDocumentImages + multiValue, err := proto.Marshal(&yotiprotoattr.MultiValue{}) + assert.NilError(t, err) + + protoAttribute := &yotiprotoattr.Attribute{ + Name: documentImagesName, + Value: multiValue, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: make([]*yotiprotoattr.Anchor, 0), + } + + result := createProfileWithSingleAttribute(protoAttribute) + + DoB, err := result.DateOfBirth() + assert.Check(t, DoB == nil) + assert.Check(t, err == nil) + assert.Check(t, result.Address() == nil) +} + +func TestMissingPostalAddress_UsesFormattedAddress(t *testing.T) { + var formattedAddressText = `House No.86-A\nRajgura Nagar\nLudhina\nPunjab\n141012\nIndia` + + var structuredAddressBytes = []byte(` + { + "address_format": 2, + "building": "House No.86-A", + "formatted_address": "` + formattedAddressText + `" + } + `) + + var jsonAttribute = &yotiprotoattr.Attribute{ + Name: consts.AttrStructuredPostalAddress, + Value: structuredAddressBytes, + ContentType: yotiprotoattr.ContentType_JSON, + Anchors: []*yotiprotoattr.Anchor{}, + } + + profile := createProfileWithSingleAttribute(jsonAttribute) + + ensureAddressProfile(&profile) + + escapedFormattedAddressText := strings.Replace(formattedAddressText, `\n`, "\n", -1) + + profileAddress := profile.Address().Value() + assert.Equal(t, profileAddress, escapedFormattedAddressText, "Address does not equal the expected formatted address.") + + structuredPostalAddress, err := profile.StructuredPostalAddress() + assert.NilError(t, err) + assert.Equal(t, structuredPostalAddress.ContentType(), "JSON") +} + +func TestAttributeImage_Image_Png(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.DeepEqual(t, selfie.Value().Data(), attributeValue) +} + +func TestAttributeImage_Image_Jpeg(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_JPEG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.DeepEqual(t, selfie.Value().Data(), attributeValue) +} + +func TestAttributeImage_Image_Default(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + selfie := result.Selfie() + + assert.DeepEqual(t, selfie.Value().Data(), attributeValue) +} +func TestAttributeImage_Base64Selfie_Png(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_PNG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(attributeValue) + expectedBase64Selfie := "data:image/png;base64," + base64ImageExpectedValue + base64Selfie := result.Selfie().Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} + +func TestAttributeImage_Base64URL_Jpeg(t *testing.T) { + attributeImage := createSelfieAttribute(yotiprotoattr.ContentType_JPEG, "id") + + result := createProfileWithSingleAttribute(attributeImage) + + base64ImageExpectedValue := base64.StdEncoding.EncodeToString(attributeValue) + + expectedBase64Selfie := "data:image/jpeg;base64," + base64ImageExpectedValue + + base64Selfie := result.Selfie().Value().Base64URL() + + assert.Equal(t, base64Selfie, expectedBase64Selfie) +} + +func TestProfile_IdentityProfileReport_RetrievesAttribute(t *testing.T) { + identityProfileReportJSON, err := file.ReadFile("../test/fixtures/RTWIdentityProfileReport.json") + assert.NilError(t, err) + + var attr = &yotiprotoattr.Attribute{ + Name: consts.AttrIdentityProfileReport, + Value: identityProfileReportJSON, + ContentType: yotiprotoattr.ContentType_JSON, + Anchors: []*yotiprotoattr.Anchor{}, + } + + result := createProfileWithSingleAttribute(attr) + att, err := result.IdentityProfileReport() + assert.NilError(t, err) + + retrievedIdentityProfile := att.Value() + gotProof := retrievedIdentityProfile["proof"] + + assert.Equal(t, gotProof, "") +} + +func TestProfileAllowsMultipleAttributesWithSameName(t *testing.T) { + firstAttribute := createStringAttribute("full_name", []byte("some_value"), []*yotiprotoattr.Anchor{}, "id") + secondAttribute := createStringAttribute("full_name", []byte("some_other_value"), []*yotiprotoattr.Anchor{}, "id") + + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, firstAttribute, secondAttribute) + + var profile = UserProfile{ + baseProfile{ + attributeSlice: attributeSlice, + }, + } + + var fullNames = profile.GetAttributes("full_name") + + assert.Assert(t, is.Equal(len(fullNames), 2)) + assert.Assert(t, is.Equal(fullNames[0].Value().(string), "some_value")) + assert.Assert(t, is.Equal(fullNames[1].Value().(string), "some_other_value")) +} + +func createStringAttribute(name string, value []byte, anchors []*yotiprotoattr.Anchor, attributeID string) *yotiprotoattr.Attribute { + return &yotiprotoattr.Attribute{ + Name: name, + Value: value, + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: anchors, + EphemeralId: attributeID, + } +} + +func createSelfieAttribute(contentType yotiprotoattr.ContentType, attributeID string) *yotiprotoattr.Attribute { + var attributeImage = &yotiprotoattr.Attribute{ + Name: consts.AttrSelfie, + Value: attributeValue, + ContentType: contentType, + Anchors: []*yotiprotoattr.Anchor{}, + EphemeralId: attributeID, + } + return attributeImage +} + +func createDocumentImagesAttribute(attributeID string) *yotiprotoattr.Attribute { + multiValue, err := proto.Marshal(&yotiprotoattr.MultiValue{}) + if err != nil { + panic(err) + } + + protoAttribute := &yotiprotoattr.Attribute{ + Name: consts.AttrDocumentImages, + Value: multiValue, + ContentType: yotiprotoattr.ContentType_MULTI_VALUE, + Anchors: make([]*yotiprotoattr.Anchor, 0), + EphemeralId: attributeID, + } + return protoAttribute +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/client.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/client.go new file mode 100644 index 0000000..74c289e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/client.go @@ -0,0 +1,10 @@ +package requests + +import ( + "net/http" +) + +// HttpClient is a mockable HTTP Client Interface +type HttpClient interface { + Do(*http.Request) (*http.Response, error) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request.go new file mode 100644 index 0000000..b1fb602 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request.go @@ -0,0 +1,38 @@ +package requests + +import ( + "net/http" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotierror" +) + +// Execute makes a request to the specified endpoint, with an optional payload +func Execute(httpClient HttpClient, request *http.Request, httpErrorMessages ...map[int]string) (response *http.Response, err error) { + if response, err = doRequest(request, httpClient); err != nil { + return + } + + statusCodeIsFailure := response.StatusCode >= 300 || response.StatusCode < 200 + + if statusCodeIsFailure { + return response, yotierror.NewResponseError(response, httpErrorMessages...) + } + + return response, nil +} + +func doRequest(request *http.Request, httpClient HttpClient) (*http.Response, error) { + httpClient = ensureHttpClientTimeout(httpClient) + return httpClient.Do(request) +} + +func ensureHttpClientTimeout(httpClient HttpClient) HttpClient { + if httpClient == nil { + httpClient = &http.Client{ + Timeout: time.Second * 10, + } + } + + return httpClient +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request_test.go new file mode 100644 index 0000000..0137f2f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/request_test.go @@ -0,0 +1,90 @@ +package requests + +import ( + "errors" + "net/http" + "testing" + "time" + + "gotest.tools/v3/assert" +) + +type mockHTTPClient struct { + do func(*http.Request) (*http.Response, error) +} + +func (mock *mockHTTPClient) Do(request *http.Request) (*http.Response, error) { + if mock.do != nil { + return mock.do(request) + } + return nil, nil +} + +func TestExecute_Success(t *testing.T) { + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 200, + }, nil + }, + } + + request := &http.Request{ + Method: http.MethodGet, + } + + response, err := Execute(client, request) + + assert.NilError(t, err) + assert.Equal(t, response.StatusCode, 200) +} + +func TestExecute_Failure(t *testing.T) { + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: 400, + }, nil + }, + } + + request := &http.Request{ + Method: http.MethodGet, + } + + response, err := Execute(client, request) + + assert.ErrorContains(t, err, "400: unknown HTTP error") + assert.Equal(t, response.StatusCode, 400) +} + +func TestExecute_ClientError(t *testing.T) { + client := &mockHTTPClient{ + do: func(*http.Request) (*http.Response, error) { + return nil, errors.New("some error") + }, + } + + request := &http.Request{ + Method: http.MethodGet, + } + + _, err := Execute(client, request) + + assert.ErrorContains(t, err, "some error") +} + +func TestEnsureHttpClientTimeout_NilHTTPClientShouldUse10sTimeout(t *testing.T) { + result := ensureHttpClientTimeout(nil).(*http.Client) + + assert.Equal(t, 10*time.Second, result.Timeout) +} + +func TestEnsureHttpClientTimeout(t *testing.T) { + httpClient := &http.Client{ + Timeout: time.Minute * 12, + } + result := ensureHttpClientTimeout(httpClient).(*http.Client) + + assert.Equal(t, 12*time.Minute, result.Timeout) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message.go new file mode 100644 index 0000000..e90fc54 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message.go @@ -0,0 +1,215 @@ +package requests + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/consts" +) + +// MergeHeaders merges two or more header prototypes together from left to right +func MergeHeaders(headers ...map[string][]string) map[string][]string { + if len(headers) == 0 { + return make(map[string][]string) + } + out := headers[0] + for _, element := range headers[1:] { + for k, v := range element { + out[k] = v + } + } + return out +} + +// JSONHeaders is a header prototype for JSON based requests +func JSONHeaders() map[string][]string { + return map[string][]string{ + "Content-Type": {"application/json"}, + "Accept": {"application/json"}, + } +} + +// AuthKeyHeader is a header prototype including an encoded RSA PublicKey +func AuthKeyHeader(key *rsa.PublicKey) map[string][]string { + return map[string][]string{ + "X-Yoti-Auth-Key": { + base64.StdEncoding.EncodeToString( + func(a []byte, _ error) []byte { + return a + }(x509.MarshalPKIXPublicKey(key)), + ), + }, + } +} + +// SignedRequest is a builder for constructing a http.Request with Yoti signing +type SignedRequest struct { + Key *rsa.PrivateKey + HTTPMethod string + BaseURL string + Endpoint string + Headers map[string][]string + Params map[string]string + Body []byte + Error error +} + +func (msg *SignedRequest) signDigest(digest []byte) (string, error) { + hash := sha256.Sum256(digest) + signed, err := rsa.SignPKCS1v15(rand.Reader, msg.Key, crypto.SHA256, hash[:]) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(signed), nil +} + +func getTimestamp() string { + return strconv.FormatInt(time.Now().Unix()*1000, 10) +} + +func getNonce() (string, error) { + nonce := make([]byte, 16) + _, err := rand.Read(nonce) + return fmt.Sprintf("%X-%X-%X-%X-%X", nonce[0:4], nonce[4:6], nonce[6:8], nonce[8:10], nonce[10:]), err +} + +// WithPemFile loads the private key from a PEM file reader +func (msg SignedRequest) WithPemFile(in []byte) SignedRequest { + block, _ := pem.Decode(in) + if block == nil { + msg.Error = errors.New("input is not PEM-encoded") + return msg + } + if block.Type != "RSA PRIVATE KEY" { + msg.Error = errors.New("input is not an RSA Private Key") + return msg + } + + msg.Key, msg.Error = x509.ParsePKCS1PrivateKey(block.Bytes) + return msg +} + +func (msg *SignedRequest) addParametersToEndpoint() (string, error) { + if msg.Params == nil { + msg.Params = make(map[string]string) + } + // Add Timestamp/Nonce + if _, ok := msg.Params["nonce"]; !ok { + nonce, err := getNonce() + if err != nil { + return "", err + } + msg.Params["nonce"] = nonce + } + if _, ok := msg.Params["timestamp"]; !ok { + msg.Params["timestamp"] = getTimestamp() + } + + endpoint := msg.Endpoint + if !strings.Contains(endpoint, "?") { + endpoint = endpoint + "?" + } else { + endpoint = endpoint + "&" + } + + var firstParam = true + for param, value := range msg.Params { + var formatString = "%s&%s=%s" + if firstParam { + formatString = "%s%s=%s" + } + endpoint = fmt.Sprintf(formatString, endpoint, param, value) + firstParam = false + } + + return endpoint, nil +} + +func (msg *SignedRequest) generateDigest(endpoint string) (digest string) { + // Generate the message digest + if msg.Body != nil { + digest = fmt.Sprintf( + "%s&%s&%s", + msg.HTTPMethod, + endpoint, + base64.StdEncoding.EncodeToString(msg.Body), + ) + } else { + digest = fmt.Sprintf("%s&%s", + msg.HTTPMethod, + endpoint, + ) + } + return +} + +func (msg *SignedRequest) checkMandatories() error { + if msg.Error != nil { + return msg.Error + } + if msg.Key == nil { + return fmt.Errorf("missing private key") + } + if msg.HTTPMethod == "" { + return fmt.Errorf("missing HTTPMethod") + } + if msg.BaseURL == "" { + return fmt.Errorf("missing BaseURL") + } + if msg.Endpoint == "" { + return fmt.Errorf("missing Endpoint") + } + return nil +} + +// Request builds a http.Request with signature headers +func (msg SignedRequest) Request() (request *http.Request, err error) { + err = msg.checkMandatories() + if err != nil { + return + } + + endpoint, err := msg.addParametersToEndpoint() + if err != nil { + return + } + + signedDigest, err := msg.signDigest([]byte(msg.generateDigest(endpoint))) + if err != nil { + return + } + + // Construct the HTTP Request + request, err = http.NewRequest( + msg.HTTPMethod, + msg.BaseURL+endpoint, + bytes.NewReader(msg.Body), + ) + if err != nil { + return + } + + request.Header.Add("X-Yoti-Auth-Digest", signedDigest) + request.Header.Add("X-Yoti-SDK", consts.SDKIdentifier) + request.Header.Add("X-Yoti-SDK-Version", consts.SDKVersionIdentifier) + + for key, values := range msg.Headers { + for _, value := range values { + request.Header.Add(key, value) + } + } + + return request, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message_test.go new file mode 100644 index 0000000..1e23205 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/requests/signed_message_test.go @@ -0,0 +1,169 @@ +package requests + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/json" + "fmt" + "math/rand" + "net/http" + "regexp" + "testing" + + "gotest.tools/v3/assert" +) + +const exampleKey = "MIICXgIBAAKBgQCpTiICtL+ujx8D0FquVWIaXg+ajJadN5hsTlGUXymiFAunSZjLjTsoGfSPz8PJm6pG9ax1Qb+R5UsSgTRTcpZTps2RLRWr5oPfD66bz4l38QXPSvfg5o+5kNxyCb8QANitF7Ht/DcpsGpL7anruHg/RgCLCBFRaGAodfuJCCM9zwIDAQABAoGBAIJL7GbSvjZUVVU1E6TZd0+9lhqmGf/S2o5309bxSfQ/oxxSyrHU9nMNTqcjCZXuJCTKS7hOKmXY5mbOYvvZ0xA7DXfOc+A4LGXQl0r3ZMzhHZTPKboUSh16E4WI4pr98KagFdkeB/0KBURM3x5d/6dSKip8ZpEyqVpuc9d1xtvhAkEAxabfsqfb4fgBsrhZ/qt133yB0FBHs1alRxvUXZWbVPTOegKi5KBdPptf2QfCy8WK3An/lg8cFQG78PyNll/P0QJBANtJBUHTuRDCoYLhqZLdSTQ52qOWRNutZ2fho9ZcLquokB4SFFeC2I4T+s3oSJ8SNh9vW1nNeXW6Zipx+zz8O58CQQCjV9qNGf40zDITEhmFxwt967aYgpAO3O9wScaCpM4fMsWkvaMDEKiewec/RBOvNY0hdb3ctJX/olRAv2b/vCTRAkAuLmCnDlnJR9QP5kp6HZRPJWgAT6NMyGYgoIqKmHtTt3oyewhBrdLBiT+moaa5qXIwiJkqfnV377uYcMzCeTRtAkEAwHdhM3v01GprmHqE2kvlKOXNq9CB1Z4j/vXSQxBYoSrFWLv5nW9e69ngX+n7qhvO3Gs9CBoy/oqOLatFZOuFEw==" + +var keyBytes, _ = base64.StdEncoding.DecodeString(exampleKey) +var privateKey, _ = x509.ParsePKCS1PrivateKey(keyBytes) + +func ExampleMergeHeaders() { + left := map[string][]string{"A": {"Value Of A"}} + right := map[string][]string{"B": {"Value Of B"}} + + merged := MergeHeaders(left, right) + fmt.Println(merged["A"]) + fmt.Println(merged["B"]) + // Output: + // [Value Of A] + // [Value Of B] +} + +func TestMergeHeaders_HandleNullCaseGracefully(t *testing.T) { + assert.Equal(t, len(MergeHeaders()), 0) +} + +func ExampleJSONHeaders() { + jsonHeaders, err := json.Marshal(JSONHeaders()) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(jsonHeaders)) + // Output: {"Accept":["application/json"],"Content-Type":["application/json"]} +} + +func ExampleAuthKeyHeader() { + headers, err := json.Marshal(AuthKeyHeader(&privateKey.PublicKey)) + if err != nil { + fmt.Printf("error: %s", err.Error()) + return + } + + fmt.Println(string(headers)) + // Output: {"X-Yoti-Auth-Key":["MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpTiICtL+ujx8D0FquVWIaXg+ajJadN5hsTlGUXymiFAunSZjLjTsoGfSPz8PJm6pG9ax1Qb+R5UsSgTRTcpZTps2RLRWr5oPfD66bz4l38QXPSvfg5o+5kNxyCb8QANitF7Ht/DcpsGpL7anruHg/RgCLCBFRaGAodfuJCCM9zwIDAQAB"]} +} + +func TestRequestShouldBuildForValid(t *testing.T) { + random := rand.New(rand.NewSource(25)) + key, err := rsa.GenerateKey(random, 1024) + + assert.NilError(t, err) + httpMethod := "GET" + baseURL := "example.com" + endpoint := "/" + + request := SignedRequest{ + Key: key, + HTTPMethod: httpMethod, + BaseURL: baseURL, + Endpoint: endpoint, + } + signed, err := request.Request() + assert.NilError(t, err) + assert.Equal(t, httpMethod, signed.Method) + urlCheck, err := regexp.Match(baseURL+endpoint, []byte(signed.URL.String())) + assert.NilError(t, err) + assert.Check(t, urlCheck) + assert.Check(t, signed.Header.Get("X-Yoti-Auth-Digest") != "") + assert.Equal(t, signed.Header.Get("X-Yoti-SDK"), "Go") + assert.Equal(t, signed.Header.Get("X-Yoti-SDK-Version"), "3.14.0") +} + +func TestRequestShouldAddHeaders(t *testing.T) { + random := rand.New(rand.NewSource(25)) + key, err := rsa.GenerateKey(random, 1024) + + assert.NilError(t, err) + httpMethod := "GET" + baseURL := "example.com" + endpoint := "/" + + request := SignedRequest{ + Key: key, + HTTPMethod: httpMethod, + BaseURL: baseURL, + Endpoint: endpoint, + Headers: JSONHeaders(), + } + signed, err := request.Request() + assert.NilError(t, err) + assert.Check(t, signed.Header["X-Yoti-Auth-Digest"][0] != "") + assert.Equal(t, signed.Header["Accept"][0], "application/json") +} + +func TestSignedRequest_checkMandatories_WhenErrorIsSetReturnIt(t *testing.T) { + msg := &SignedRequest{Error: fmt.Errorf("exampleError")} + assert.Error(t, msg.checkMandatories(), "exampleError") +} + +func TestSignedRequest_checkMandatories_WhenKeyMissing(t *testing.T) { + msg := &SignedRequest{} + assert.Error(t, msg.checkMandatories(), "missing private key") +} + +func TestSignedRequest_checkMandatories_WhenHTTPMethodMissing(t *testing.T) { + msg := &SignedRequest{Key: privateKey} + assert.Error(t, msg.checkMandatories(), "missing HTTPMethod") +} + +func TestSignedRequest_checkMandatories_WhenBaseURLMissing(t *testing.T) { + msg := &SignedRequest{ + Key: privateKey, + HTTPMethod: http.MethodPost, + } + assert.Error(t, msg.checkMandatories(), "missing BaseURL") +} + +func TestSignedRequest_checkMandatories_WhenEndpointMissing(t *testing.T) { + msg := &SignedRequest{ + Key: privateKey, + HTTPMethod: http.MethodPost, + BaseURL: "example.com", + } + assert.Error(t, msg.checkMandatories(), "missing Endpoint") +} + +func ExampleSignedRequest_generateDigest() { + msg := &SignedRequest{ + HTTPMethod: http.MethodPost, + Body: []byte("simple message body"), + } + fmt.Println(msg.generateDigest("endpoint")) + // Output: POST&endpoint&c2ltcGxlIG1lc3NhZ2UgYm9keQ== + +} + +func ExampleSignedRequest_WithPemFile() { + msg := SignedRequest{}.WithPemFile([]byte(` +-----BEGIN RSA PRIVATE KEY----- +` + exampleKey + ` +-----END RSA PRIVATE KEY-----`)) + fmt.Println(AuthKeyHeader(&msg.Key.PublicKey)) + // Output: map[X-Yoti-Auth-Key:[MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCpTiICtL+ujx8D0FquVWIaXg+ajJadN5hsTlGUXymiFAunSZjLjTsoGfSPz8PJm6pG9ax1Qb+R5UsSgTRTcpZTps2RLRWr5oPfD66bz4l38QXPSvfg5o+5kNxyCb8QANitF7Ht/DcpsGpL7anruHg/RgCLCBFRaGAodfuJCCM9zwIDAQAB]] +} + +func TestSignedRequest_WithPemFile_NotPemEncodedShouldError(t *testing.T) { + msg := SignedRequest{}.WithPemFile([]byte("not pem encoded")) + assert.ErrorContains(t, msg.Error, "not PEM-encoded") +} + +func TestSignedRequest_WithPemFile_NotRSAKeyShouldError(t *testing.T) { + msg := SignedRequest{}.WithPemFile([]byte(`-----BEGIN RSA PUBLIC KEY----- +` + exampleKey + ` +-----END RSA PUBLIC KEY-----`)) + assert.ErrorContains(t, msg.Error, "not an RSA Private Key") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/go-build-modtidy.sh b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/go-build-modtidy.sh new file mode 100644 index 0000000..bef8763 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/go-build-modtidy.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +go build ./... + +for d in _examples/*/; do + (cd "$d" && go mod tidy -compat=1.19) +done diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/gofmt.sh b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/gofmt.sh new file mode 100644 index 0000000..e7cee65 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/gofmt.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +unset dirs files +dirs=$(go list -f {{.Dir}} ./... | grep -v /yotiprotoshare/ | grep -v /yotiprotocom/ | grep -v /yotiprotoattr/) +for d in $dirs; do + for f in $d/*.go; do + files="${files} $f" + done +done +if [ -n "$(gofmt -d $files)" ]; then + echo "Go code is not formatted:" + gofmt -d . + exit 1 +fi diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/goimports.sh b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/goimports.sh new file mode 100644 index 0000000..45e2c24 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/goimports.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e +for FILE in $@; do + echo $FILE | if grep --quiet *.go; then + goimports -w $FILE + fi +done diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/test.sh b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/test.sh new file mode 100644 index 0000000..0bebfb8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sh/test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +set -e +go test -race ./... diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sonar-project.properties b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sonar-project.properties new file mode 100644 index 0000000..b0407bb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/sonar-project.properties @@ -0,0 +1,15 @@ +sonar.organization = getyoti +sonar.projectKey = getyoti:go +sonar.projectName = Go SDK +sonar.projectVersion = 3.14.0 +sonar.exclusions = **/yotiprotoattr/*.go,**/yotiprotocom/*.go,**/yotiprotoshare/*.go,**/**_test.go,_examples/**/* +sonar.links.scm = https://github.com/getyoti/yoti-go-sdk +sonar.host.url = https://sonarcloud.io + +sonar.sources = . +sonar.go.coverage.reportPaths = coverage.out +sonar.go.tests.reportPaths = sonar-report.json +sonar.tests = . +sonar.test.inclusions = **/*_test.go +sonar.coverage.exclusions = test/**/*,_examples/**/* +sonar.cpd.exclusions = digitalidentity/** diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/attribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/attribute.go new file mode 100644 index 0000000..1e3787e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/attribute.go @@ -0,0 +1,41 @@ +package test + +import ( + "testing" + "time" + + "github.com/getyoti/yoti-go-sdk/v3/yotiprotoshare" + "google.golang.org/protobuf/proto" + + "gotest.tools/v3/assert" +) + +// CreateThirdPartyAttributeDataEntry creates a data entry of type "THIRD_PARTY_ATTRIBUTE", with the specified IssuingAttribute details. +func CreateThirdPartyAttributeDataEntry(t *testing.T, expiryDate *time.Time, stringDefinitions []string, tokenValue string) yotiprotoshare.DataEntry { + var protoDefinitions []*yotiprotoshare.Definition + + for _, definition := range stringDefinitions { + protoDefinition := &yotiprotoshare.Definition{ + Name: definition, + } + + protoDefinitions = append(protoDefinitions, protoDefinition) + } + + thirdPartyAttribute := &yotiprotoshare.ThirdPartyAttribute{ + IssuanceToken: []byte(tokenValue), + IssuingAttributes: &yotiprotoshare.IssuingAttributes{ + ExpiryDate: expiryDate.Format("2006-01-02T15:04:05.000Z"), + Definitions: protoDefinitions, + }, + } + + marshalledThirdPartyAttribute, err := proto.Marshal(thirdPartyAttribute) + + assert.NilError(t, err) + + return yotiprotoshare.DataEntry{ + Type: yotiprotoshare.DataEntry_THIRD_PARTY_ATTRIBUTE, + Value: marshalledThirdPartyAttribute, + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/constants.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/constants.go new file mode 100644 index 0000000..76e8a28 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/constants.go @@ -0,0 +1,7 @@ +package test + +const ( + Token = "NpdmVVGC-28356678-c236-4518-9de4-7a93009ccaf0-c5f92f2a-5539-453e-babc-9b06e1d6b7de" + EncryptedToken = "b6H19bUCJhwh6WqQX_sEHWX9RP-A_ANr1fkApwA4Dp2nJQFAjrF9e6YCXhNBpAIhfHnN0iXubyXxXZMNwNMSQ5VOxkqiytrvPykfKQWHC6ypSbfy0ex8ihndaAXG5FUF-qcU8QaFPMy6iF3x0cxnY0Ij0kZj0Ng2t6oiNafb7AhT-VGXxbFbtZu1QF744PpWMuH0LVyBsAa5N5GJw2AyBrnOh67fWMFDKTJRziP5qCW2k4h5vJfiYr_EOiWKCB1d_zINmUm94ZffGXxcDAkq-KxhN1ZuNhGlJ2fKcFh7KxV0BqlUWPsIEiwS0r9CJ2o1VLbEs2U_hCEXaqseEV7L29EnNIinEPVbL4WR7vkF6zQCbK_cehlk2Qwda-VIATqupRO5grKZN78R9lBitvgilDaoE7JB_VFcPoljGQ48kX0wje1mviX4oJHhuO8GdFITS5LTbojGVQWT7LUNgAUe0W0j-FLHYYck3v84OhWTqads5_jmnnLkp9bdJSRuJF0e8pNdePnn2lgF-GIcyW_0kyGVqeXZrIoxnObLpF-YeUteRBKTkSGFcy7a_V_DLiJMPmH8UXDLOyv8TVt3ppzqpyUrLN2JVMbL5wZ4oriL2INEQKvw_boDJjZDGeRlu5m1y7vGDNBRDo64-uQM9fRUULPw-YkABNwC0DeShswzT00=" + 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=" +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/decode_test_file.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/decode_test_file.go new file mode 100644 index 0000000..d1dde76 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/decode_test_file.go @@ -0,0 +1,19 @@ +package test + +import ( + "encoding/base64" + "testing" + + "gotest.tools/v3/assert" +) + +// DecodeTestFile reads a test fixture file +func DecodeTestFile(t *testing.T, filename string) (result []byte) { + base64Bytes := readTestFile(t, filename) + base64String := string(base64Bytes) + filebytes, err := base64.StdEncoding.DecodeString(base64String) + + assert.NilError(t, err) + + return filebytes +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fileparser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fileparser.go new file mode 100644 index 0000000..86c8318 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fileparser.go @@ -0,0 +1,33 @@ +package test + +import ( + "encoding/base64" + "io/ioutil" + "testing" + + "gotest.tools/v3/assert" +) + +// GetTestFileBytes takes a filepath, decodes it from base64, and returns a byte representation of it +func GetTestFileBytes(t *testing.T, filename string) (result []byte) { + base64Bytes := readTestFile(t, filename) + base64String := string(base64Bytes) + filebytes, err := base64.StdEncoding.DecodeString(base64String) + + assert.NilError(t, err) + + return filebytes +} + +// GetTestFileAsString returns a file as a string +func GetTestFileAsString(t *testing.T, filename string) string { + base64Bytes := readTestFile(t, filename) + return string(base64Bytes) +} + +func readTestFile(t *testing.T, filename string) (result []byte) { + b, err := ioutil.ReadFile(filename) + assert.NilError(t, err) + + return b +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithAdvancedIdentityProfile.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithAdvancedIdentityProfile.json new file mode 100644 index 0000000..8cfe688 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithAdvancedIdentityProfile.json @@ -0,0 +1,51 @@ +{ + "session_id": "a1746488-efcc-4c59-bd28-f849dcb933a2", + "client_session_token_ttl": 599, + "user_tracking_id": "user-tracking-id", + "biometric_consent": "2022-03-29T11:39:08.473Z", + "state": "COMPLETED", + "client_session_token": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "advanced_identity_profile": { + "subject_id": "someStringHere", + "result": "DONE", + "failure_reason": { + "reason_code": "MANDATORY_DOCUMENT_COULD_NOT_BE_PROVIDED", + "requirements_not_met_details" : [ + { + "failure_type": "ID_DOCUMENT_AUTHENTICITY", + "document_type" : "PASSPORT", + "document_country_iso_code":"GBR", + "audit_id":"a526df5f-a9c1-4e57-8aa3-919256d8e280", + "details": "INCORRECT_DOCUMENT_TYPE" + } + ] + }, + "identity_profile_report": { + "compliance": [{ + "trust_framework": "UK_TFIDA", + "schemes_compliance": [{ + "scheme": { + "type": "DBS", + "objective": "STANDARD" + }, + "requirements_met": true, + "requirements_not_met_info": "some string here" + }] + }], + "media": { + "id": "c69ff2db-6caf-4e74-8386-037711bbc8d7", + "type": "IMAGE", + "created": "2022-03-29T11:39:24Z", + "last_updated": "2022-03-29T11:39:24Z" + } + } + }, + "advanced_identity_profile_preview": { + "media": { + "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type": "IMAGE", + "created": "2021-06-11T11:39:24Z", + "last_updated": "2021-06-11T11:39:24Z" + } + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithIdentityProfile.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithIdentityProfile.json new file mode 100644 index 0000000..44b16e6 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/GetSessionResultWithIdentityProfile.json @@ -0,0 +1,49 @@ +{ + "session_id": "a1746488-efcc-4c59-bd28-f849dcb933a2", + "client_session_token_ttl": 599, + "user_tracking_id": "user-tracking-id", + "biometric_consent": "2022-03-29T11:39:08.473Z", + "state": "COMPLETED", + "client_session_token": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "identity_profile": { + "subject_id": "someStringHere", + "result": "DONE", + "failure_reason": { + "reason_code": "MANDATORY_DOCUMENT_COULD_NOT_BE_PROVIDED", + "requirements_not_met_details" : [ + { + "failure_type": "ID_DOCUMENT_AUTHENTICITY", + "document_type" : "PASSPORT", + "document_country_iso_code":"GBR", + "audit_id":"a526df5f-a9c1-4e57-8aa3-919256d8e280", + "details": "INCORRECT_DOCUMENT_TYPE" + } + ] + }, + "identity_profile_report": { + "trust_framework": "UK_TFIDA", + "schemes_compliance": [{ + "scheme": { + "type": "DBS", + "objective": "STANDARD" + }, + "requirements_met": true, + "requirements_not_met_info": "some string here" + }], + "media": { + "id": "c69ff2db-6caf-4e74-8386-037711bbc8d7", + "type": "IMAGE", + "created": "2022-03-29T11:39:24Z", + "last_updated": "2022-03-29T11:39:24Z" + } + } + }, + "identity_profile_preview": { + "media": { + "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "type": "IMAGE", + "created": "2021-06-11T11:39:24Z", + "last_updated": "2021-06-11T11:39:24Z" + } + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/RTWIdentityProfileReport.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/RTWIdentityProfileReport.json new file mode 100644 index 0000000..c389acc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/RTWIdentityProfileReport.json @@ -0,0 +1,118 @@ +{ + "identity_assertion": { + "current_name": { + "given_names": "JOHN JIM FRED", + "first_name": "JOHN", + "middle_name": "JIM FRED", + "family_name": "FOO", + "full_name": "JOHN JIM FRED FOO" + }, + "date_of_birth": "1979-01-01" + }, + "verification_report": { + "report_id": "61b99534-116d-4a25-9750-2d708c0fb168", + "timestamp": "2022-01-02T15:04:05Z", + "subject_id": "f0726cb6-97c1-4802-9feb-eb7c6cd07949", + "trust_framework": "UK_TFIDA", + "schemes_compliance": [ + { + "scheme": { + "type": "RTW" + }, + "requirements_met": true + } + ], + "assurance_process": { + "level_of_assurance": "MEDIUM", + "policy": "GPG45", + "procedure": "M1C", + "assurance": [ + { + "type": "EVIDENCE_STRENGTH", + "classification": "4", + "evidence_links": [ + "41960172-ca91-487c-8bb3-2c547f80fe54" + ] + }, + { + "type": "EVIDENCE_VALIDITY", + "classification": "3", + "evidence_links": [ + "41960172-ca91-487c-8bb3-2c547f80fe54" + ] + }, + { + "type": "VERIFICATION", + "classification": "3", + "evidence_links": [ + "41960172-ca91-487c-8bb3-2c547f80fe54", + "fb0880ca-5dd5-4776-badb-17549123c50b" + ] + } + ] + }, + "evidence": { + "documents": [ + { + "evidence_id": "41960172-ca91-487c-8bb3-2c547f80fe54", + "timestamp": "2022-01-02T15:04:05Z", + "document_fields": { + "full_name": "JOHN JIM FRED FOO", + "date_of_birth": "1979-01-01", + "nationality": "GBR", + "given_names": "JOHN JIM FRED", + "family_name": "FOO", + "place_of_birth": "SAMPLETOWN", + "gender": "MALE", + "document_type": "PASSPORT", + "issuing_country": "GBR", + "document_number": "123456789", + "expiration_date": "2030-01-01", + "date_of_issue": "2020-01-01", + "issuing_authority": "HMPO", + "mrz": { + "type": 2, + "line1": "P" +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container-static.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container-static.json new file mode 100644 index 0000000..7021a74 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container-static.json @@ -0,0 +1,52 @@ +{ + "liveness_capture": [ + { + "id": "1bf432f3-63fd-4fa7-b243-84adc305dd5f", + "tasks": [], + "source": { + "type": "END_USER" + }, + "image": { + "media": { + "id": "b73c5b58-cd54-4784-8a58-af936f31994f", + "type": "IMAGE", + "created": "2023-01-04T13:42:34Z", + "last_updated": "2023-01-04T13:42:34Z" + } + }, + "liveness_type": "STATIC" + }, + { + "id": "1fb5120f-7e50-4c48-8753-8311f4a96ed4", + "tasks": [], + "source": { + "type": "END_USER" + }, + "image": { + "media": { + "id": "72bfb8eb-2333-4c24-8e39-cd67c408e68c", + "type": "IMAGE", + "created": "2023-01-04T13:42:25Z", + "last_updated": "2023-01-04T13:42:25Z" + } + }, + "liveness_type": "STATIC" + }, + { + "id": "ea28dd83-c273-4d59-a91f-fd2d0df06640", + "tasks": [], + "source": { + "type": "END_USER" + }, + "image": { + "media": { + "id": "e95bc897-a8cf-4c21-ba87-4415571d1a55", + "type": "IMAGE", + "created": "2023-01-04T13:42:15Z", + "last_updated": "2023-01-04T13:42:15Z" + } + }, + "liveness_type": "STATIC" + } +] +} \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container.json new file mode 100644 index 0000000..9b473a4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/resource-container.json @@ -0,0 +1,30 @@ +{ + "liveness_capture": [{ + "id": "a831bc40-e3c2-11ea-87d0-0242ac130003", + "liveness_type": "ZOOM", + "facemap": { + "media": { + "id": "abd059e2-e3c2-11ea-87d0-0242ac130003", + "type": "BINARY", + "created": "2020-08-21T16:25:00Z", + "last_updated": "2020-08-21T16:25:00Z" + } + }, + "frames": [{ + "media": { + "id": "b1177368-e3c2-11ea-87d0-0242ac130003", + "type": "IMAGE", + "created": "2020-08-21T16:25:00Z", + "last_updated": "2020-08-21T16:25:00Z" + } + }, { + "media": null + } + ] + }, { + "id": "d2677368-e3c2-11ea-87d0-0242ac139182", + "liveness_type": "OTHER_LIVENESS_TYPE" + } + ] +} + diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_driving_license.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_driving_license.txt new file mode 100644 index 0000000..dfe875f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_driving_license.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_passport.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_passport.txt new file mode 100644 index 0000000..237f052 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_passport.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_unknown.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_unknown.txt new file mode 100644 index 0000000..d06eb77 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_unknown.txt @@ -0,0 +1,39 @@ +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 +WWoW7TZ8Y5CjoA \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_yoti_admin.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_yoti_admin.txt new file mode 100644 index 0000000..6333815 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_anchor_yoti_admin.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_date_of_birth.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_date_of_birth.txt new file mode 100644 index 0000000..7cf4b6c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_date_of_birth.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_multivalue.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_multivalue.txt new file mode 100644 index 0000000..96c7af3 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_multivalue.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_third_party.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_third_party.txt new file mode 100644 index 0000000..4582559 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_attribute_third_party.txt @@ -0,0 +1 @@ +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 diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_extra_data.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_extra_data.txt new file mode 100644 index 0000000..25d50e1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_extra_data.txt @@ -0,0 +1 @@ +CmMIBhJfChFzb21lSXNzdWFuY2VUb2tlbhJKChgyMDE5LTEwLTE1VDIyOjA0OjA1LjEyM1oSEwoRY29tLnRoaXJkcGFydHkuaWQSGQoXY29tLnRoaXJkcGFydHkub3RoZXJfaWQ= \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_third_party_issuance_details.txt b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_third_party_issuance_details.txt new file mode 100644 index 0000000..b8fd354 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/test_third_party_issuance_details.txt @@ -0,0 +1 @@ +ChFzb21lSXNzdWFuY2VUb2tlbhIvChgyMDE5LTEwLTE1VDIyOjA0OjA1LjEyM1oSEwoRY29tLnRoaXJkcGFydHkuaWQ= \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_advanced_ca_profile_custom.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_advanced_ca_profile_custom.json new file mode 100644 index 0000000..968e4cc --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_advanced_ca_profile_custom.json @@ -0,0 +1,257 @@ +{ + "client_session_token_ttl": 155057, + "session_id": "a3819be3-df1f-4d8c-9161-abfe1b19d9e8", + "state": "COMPLETED", + "resources": { + "id_documents": [ + { + "id": "d99241db-243f-472d-84a2-d956b87db5f8", + "tasks": [ + { + "type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION", + "id": "e1995b9a-9b6f-43b9-a179-32bbe2d25586", + "state": "DONE", + "created": "2022-01-14T14:54:14Z", + "last_updated": "2022-01-14T14:59:13Z", + "generated_checks": [ + { + "id": "851cec96-7459-49d3-b9b2-bcbc4c759987", + "type": "ID_DOCUMENT_TEXT_DATA_CHECK" + } + ], + "generated_media": [ + { + "id": "a8c9e973-4e58-43d8-952d-17f36f12bce2", + "type": "JSON" + } + ] + } + ], + "source": { + "type": "END_USER" + }, + "document_type": "DRIVING_LICENCE", + "issuing_country": "GBR", + "pages": [ + { + "capture_method": "UPLOAD", + "media": { + "id": "8c65cb1c-92cb-43df-bac1-65433ac3a1c1", + "type": "IMAGE", + "created": "2022-01-14T14:54:30Z", + "last_updated": "2022-01-14T14:54:30Z" + }, + "frames": [{}, {}] + }, + { + "capture_method": "UPLOAD", + "media": { + "id": "a7c8331c-6e29-4720-8818-27c02e6252b3", + "type": "IMAGE", + "created": "2022-01-14T14:54:31Z", + "last_updated": "2022-01-14T14:54:31Z" + }, + "frames": [{}, {}] + } + ], + "document_fields": { + "media": { + "id": "b5cae0f3-ae43-41d3-b8a7-1b0d363938ef", + "type": "JSON", + "created": "2022-01-14T14:59:13Z", + "last_updated": "2022-01-14T14:59:13Z" + } + }, + "document_id_photo": { + "media": { + "id": "ee3f9895-0552-4e16-ad9a-914e2f676c10", + "type": "IMAGE", + "created": "2022-01-14T14:54:34Z", + "last_updated": "2022-01-14T14:54:34Z" + } + } + } + ], + "supplementary_documents": [], + "liveness_capture": [], + "face_capture": [] + }, + "checks": [ + { + "type": "ID_DOCUMENT_AUTHENTICITY", + "id": "f11efbfb-712c-4f9d-8328-2add190f32e3", + "state": "DONE", + "resources_used": ["d99241db-243f-472d-84a2-d956b87db5f8"], + "generated_media": [], + "report": { + "recommendation": { + "value": "APPROVE" + }, + "breakdown": [ + { + "sub_check": "doc_number_validation", + "result": "PASS", + "details": [] + }, + { + "sub_check": "document_in_date", + "result": "PASS", + "details": [] + }, + { + "sub_check": "fraud_list_check", + "result": "PASS", + "details": [] + } + ] + }, + "created": "2022-01-14T14:54:36Z", + "last_updated": "2022-01-14T14:59:14Z" + }, + { + "type": "ID_DOCUMENT_TEXT_DATA_CHECK", + "id": "851cec96-7459-49d3-b9b2-bcbc4c759987", + "state": "DONE", + "resources_used": ["d99241db-243f-472d-84a2-d956b87db5f8"], + "generated_media": [ + { + "id": "b5cae0f3-ae43-41d3-b8a7-1b0d363938ef", + "type": "JSON" + } + ], + "report": { + "recommendation": { + "value": "APPROVE" + }, + "breakdown": [ + { + "sub_check": "text_data_readable", + "result": "PASS", + "details": [] + } + ] + }, + "created": "2022-01-14T14:54:36Z", + "last_updated": "2022-01-14T14:59:13Z" + }, + { + "type": "WATCHLIST_ADVANCED_CA", + "id": "06e661c5-0e24-44ed-8f6c-8b99807efc12", + "state": "DONE", + "resources_used": ["d99241db-243f-472d-84a2-d956b87db5f8"], + "generated_media": [ + { + "id": "7c405b5e-348d-4a2c-87ff-787d4fb139c0", + "type": "JSON" + } + ], + "report": { + "recommendation": { + "value": "CONSIDER", + "reason": "POTENTIAL_MATCH" + }, + "breakdown": [ + { + "sub_check": "adverse_media", + "result": "FAIL", + "details": [ + { + "name": "number_of_hits", + "value": "251" + }, + { + "name": "closest_match", + "value": "name_exact,year_of_birth" + } + ] + }, + { + "sub_check": "custom_search", + "result": "FAIL", + "details": [] + }, + { + "sub_check": "fitness_probity", + "result": "FAIL", + "details": [ + { + "name": "number_of_hits", + "value": "3" + }, + { + "name": "closest_match", + "value": "name_exact" + } + ] + }, + { + "sub_check": "pep", + "result": "FAIL", + "details": [ + { + "name": "number_of_hits", + "value": "13" + }, + { + "name": "closest_match", + "value": "name_exact,year_of_birth" + } + ] + }, + { + "sub_check": "warning", + "result": "FAIL", + "details": [ + { + "name": "number_of_hits", + "value": "9" + }, + { + "name": "closest_match", + "value": "name_exact,year_of_birth" + } + ] + } + ], + "watchlist_summary": { + "total_hits": 100, + "search_config": { + "type": "WITH_CUSTOM_ACCOUNT", + "remove_deceased": true, + "share_url": true, + "matching_strategy": { + "type": "FUZZY", + "fuzziness": 0.6 + }, + "sources": { + "type": "PROFILE", + "search_profile": "b41d82de-9a1d-4494-97a6-8b1b9895a908" + }, + "api_key": "gQ2vf0STnF5nGy9SSdyuGJuYMFfNASmV", + "client_ref": "111111", + "monitoring": true + }, + "raw_results": { + "media": { + "id": "0ebadb40-670a-4dd8-b0cc-5079d5d74c1c", + "type": "JSON", + "created": "2022-01-14T14:59:14Z", + "last_updated": "2022-01-14T14:59:14Z" + } + }, + "associated_country_codes": ["GBR"] + } + }, + "created": "2022-01-14T14:54:36Z", + "last_updated": "2022-01-14T14:59:14Z", + "generated_profile": { + "media": { + "id": "7c405b5e-348d-4a2c-87ff-787d4fb139c0", + "type": "JSON", + "created": "2022-01-14T14:59:14Z", + "last_updated": "2022-01-14T14:59:14Z" + } + } + } + ] +} \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_screening.json b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_screening.json new file mode 100644 index 0000000..3aa3aec --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/fixtures/watchlist_screening.json @@ -0,0 +1,135 @@ +{ + "client_session_token_ttl": 8785, + "session_id": "d7445779-5e70-4a27-8fec-dd7835106e88", + "state": "COMPLETED", + "resources": { + "id_documents": [ + { + "id": "20deead7-4cb6-4921-a56c-ea10fa0a5595", + "tasks": [ + { + "type": "ID_DOCUMENT_TEXT_DATA_EXTRACTION", + "id": "738e0007-68b8-466a-bd1d-30448b157cd8", + "state": "DONE", + "created": "2021-07-20T15:14:42Z", + "last_updated": "2021-07-20T15:15:53Z", + "generated_checks": [], + "generated_media": [ + { + "id": "cfd85b6d-e78f-4c14-994c-78ad0671312d", + "type": "JSON" + } + ] + } + ], + "source": { + "type": "END_USER" + }, + "document_type": "PASSPORT", + "issuing_country": "GBR", + "pages": [ + { + "capture_method": "UPLOAD", + "media": { + "id": "6b58c356-1e68-4ee2-af54-3264406de92a", + "type": "IMAGE", + "created": "2021-07-20T15:15:52Z", + "last_updated": "2021-07-20T15:15:52Z" + }, + "frames": [{}, {}, {}] + } + ], + "document_fields": { + "media": { + "id": "cfd85b6d-e78f-4c14-994c-78ad0671312d", + "type": "JSON", + "created": "2021-07-20T15:15:53Z", + "last_updated": "2021-07-20T15:15:53Z" + } + }, + "document_id_photo": { + "media": { + "id": "d8f8051e-c5d7-4334-b5af-6b9a07fdb38c", + "type": "IMAGE", + "created": "2021-07-20T15:15:53Z", + "last_updated": "2021-07-20T15:15:53Z" + } + } + } + ], + "supplementary_documents": [], + "liveness_capture": [], + "face_capture": [] + }, + "checks": [ + { + "type": "WATCHLIST_SCREENING", + "id": "8f4a89a1-4614-47ab-9506-2c82352b66d2", + "state": "DONE", + "resources_used": ["20deead7-4cb6-4921-a56c-ea10fa0a5595"], + "generated_media": [ + { + "id": "2f5bbedd-cc1b-4d9f-a424-90bb5ee0fc99", + "type": "JSON" + } + ], + "report": { + "recommendation": { + "value": "APPROVE" + }, + "breakdown": [ + { + "sub_check": "adverse_media", + "result": "PASS", + "details": [] + }, + { + "sub_check": "fitness_probity", + "result": "PASS", + "details": [] + }, + { + "sub_check": "pep", + "result": "PASS", + "details": [] + }, + { + "sub_check": "sanction", + "result": "PASS", + "details": [] + }, + { + "sub_check": "warning", + "result": "PASS", + "details": [] + } + ], + "watchlist_summary": { + "total_hits": 0, + "search_config": { + "categories": ["ADVERSE-MEDIA", "SANCTIONS"] + }, + "raw_results": { + "media": { + "id": "b0fb459c-f573-4205-9847-5ab72301325c", + "type": "JSON", + "created": "2021-07-20T15:15:55Z", + "last_updated": "2021-07-20T15:15:55Z" + } + }, + "associated_country_codes": ["GBR"] + } + }, + "created": "2021-07-20T15:15:54Z", + "last_updated": "2021-07-20T15:15:55Z", + "generated_profile": { + "media": { + "id": "2f5bbedd-cc1b-4d9f-a424-90bb5ee0fc99", + "type": "JSON", + "created": "2021-07-20T15:15:55Z", + "last_updated": "2021-07-20T15:15:55Z" + } + } + } + ] +} \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/key.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/key.go new file mode 100644 index 0000000..54e3f49 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/key.go @@ -0,0 +1,23 @@ +package test + +import ( + "crypto/rsa" + "os" + + "github.com/getyoti/yoti-go-sdk/v3/cryptoutil" +) + +// GetValidKey returns a parsed RSA Private Key from a test key +func GetValidKey(filepath string) (key *rsa.PrivateKey) { + keyBytes, err := os.ReadFile(filepath) + if err != nil { + panic("Error reading the test key: " + err.Error()) + } + + key, err = cryptoutil.ParseRSAKey(keyBytes) + if err != nil { + panic("Error parsing the test key: " + err.Error()) + } + + return key +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key-invalid-format.pem b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key-invalid-format.pem new file mode 100644 index 0000000..dad3162 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key-invalid-format.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKQIBAAKCAgEAu7VR2P4kfOBMbsfFeaoTH7QNHVfS/VoallsiHLR9r2u52EfA +cnGELiCO8Z4I4OjMMg9yrP2Wcpyq4+pUdFW7GG2NkkPaTQpAlQ5Hm6xxgTGKahju +0OOGdLbWwol0S+QLFcnadj7/0pCSKe/v1XGR/iGZgyORHDzRMQHbLlBtMkC+wOIi +CUgnWkWhi0AJNoaEQoSvGdVAKjCOAimSbID9vGmnuK7FXCauMFRjbBh/Cbeyz4xl +w+BSvQmAGWSxpLyRinWXekNFtrm3YURwxKl4lpkEH6QQMgrImxMg+NURxNAsZob5 +QumxFopXO7ib0gNes47Ct5KyRPeF8CF+VLl6k9KWvQ3v7SYoypTXXwYfTbpqPhBr +mbPdqpIZ2oUKZJPRek68aU17YalAZ0jPNA30+UD2oKynYU2mif9UnrDxUnTnBnQX +F9tAsDWo/pOXwjKOYTKsxyBhbgh8rrgPFAqz00+qbk21gaiP4tjNMByBBMHzXUOg +GqlMjNNQhejTjL6rlXbJgmQDXPG4Xi+Q/+sUkrLNOTKY3FdnTw5PFUw9sRbP6+D6 +jS799P/OKay1DxuOPLw6r5QnfR2+pk9mNmgjVcBwwqt7gaUEjvDvj60ZppLZqQ8X +7Bv3zQetQHBtmhXyiuIH/UWXuj/VKLjnbaJtzZYTp8W4X8OT6vEwhLS9AncCAwEA +AQKCAgAIGBeBbeQQ5nMlS8P+LRFKCq+OFl1ow1vmI+PirP3GdLS82Ms5pB95Bbpk +PNZRLHixp+zf/MdiBdNwpIgjxBafRQoXxolBTTHfu4/m7JawZXx8errBky4XFlNI +bDjxlNHNjLi45JqPb+B9onULFSygcr514zC8sPqsTFIxOxKaWiRfmOCy2cOoptwC +by52hXJqk+IhEQsFRra47SX9O8q1NzEeS5sDED?uoZTv8lZ4Cs3RGVLCEYg/0osN +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 +-----END RSA PRIVATE KEY----- diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key.pem b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key.pem new file mode 100644 index 0000000..98c7641 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/test/test-key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/util/conversion.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/util/conversion.go new file mode 100644 index 0000000..4b14616 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/util/conversion.go @@ -0,0 +1,16 @@ +package util + +import ( + "encoding/base64" +) + +// Base64ToBytes converts a base64 string to bytes +func Base64ToBytes(base64Str string) ([]byte, error) { + return base64.StdEncoding.DecodeString(base64Str) +} + +// UrlSafeBase64ToBytes UrlSafe Base64 uses '-' and '_', instead of '+' and '/' respectively, so it can be passed +// as a url parameter without extra encoding. +func UrlSafeBase64ToBytes(urlSafeBase64 string) ([]byte, error) { + return base64.URLEncoding.DecodeString(urlSafeBase64) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/activitydetails.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/activitydetails.go new file mode 100644 index 0000000..1208166 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/activitydetails.go @@ -0,0 +1,14 @@ +package yotierror + +import "errors" + +var ( + // InvalidTokenError means that the token used to call GetActivityDetails is invalid. Make sure you are retrieving this token correctly. + InvalidTokenError = errors.New("invalid token") + + // TokenDecryptError means that the token could not be decrypted. Ensure you are using the correct .pem file. + TokenDecryptError = errors.New("unable to decrypt token") + + // SharingFailureError means that the share between a user and an application was not successful. + SharingFailureError = DetailedSharingFailureError{} +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error.go new file mode 100644 index 0000000..fe8d7ea --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error.go @@ -0,0 +1,21 @@ +package yotierror + +import "fmt" + +// MultiError wraps one or more errors into a single error +type MultiError struct { + This error + Next error +} + +func (e MultiError) Error() string { + if e.Next != nil { + return fmt.Sprintf("%s, %s", e.This.Error(), e.Next.Error()) + } + return e.This.Error() +} + +// Unwrap the next error +func (e MultiError) Unwrap() error { + return e.Next +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error_test.go new file mode 100644 index 0000000..fa1923f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/multi_error_test.go @@ -0,0 +1,17 @@ +package yotierror + +import ( + "errors" + "testing" + + "gotest.tools/v3/assert" +) + +func TestMultiError(t *testing.T) { + err := errors.New("inner err") + err = MultiError{This: errors.New("outer err"), Next: err} + result := err.(MultiError) + + assert.Equal(t, result.Error(), "outer err, inner err") + assert.Error(t, result.Unwrap(), "inner err") +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response.go new file mode 100644 index 0000000..75c689d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response.go @@ -0,0 +1,137 @@ +package yotierror + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" +) + +var ( + defaultUnknownErrorMessageConst = "unknown HTTP error" + + // DefaultHTTPErrorMessages maps HTTP error status codes to format strings + // to create useful error messages. -1 is used to specify a default message + // that can be used if an error code is not explicitly defined + DefaultHTTPErrorMessages = map[int]string{ + -1: defaultUnknownErrorMessageConst, + } +) + +// DataObject maps from JSON error responses +type DataObject struct { + Code string `json:"code"` + Message string `json:"message"` + Errors []ItemDataObject `json:"errors,omitempty"` +} + +// ItemDataObject maps from JSON error items +type ItemDataObject struct { + Message string `json:"message"` + Property string `json:"property"` +} + +// Error indicates errors related to the Yoti API. +type Error struct { + message string + Err error + Response *http.Response +} + +// NewResponseError creates a new Error +func NewResponseError(response *http.Response, httpErrorMessages ...map[int]string) *Error { + return &Error{ + message: formatResponseMessage(response, httpErrorMessages...), + Response: response, + } +} + +// Error return the error message +func (e Error) Error() string { + return e.message +} + +// Temporary indicates this error is a temporary error +func (e Error) Temporary() bool { + return e.Response != nil && e.Response.StatusCode >= 500 +} + +func formatResponseMessage(response *http.Response, httpErrorMessages ...map[int]string) string { + defaultMessage := handleHTTPError(response, httpErrorMessages...) + + if response == nil || response.Body == nil { + return defaultMessage + } + + body, _ := io.ReadAll(response.Body) + response.Body = io.NopCloser(bytes.NewBuffer(body)) + + var errorDO DataObject + jsonError := json.Unmarshal(body, &errorDO) + + if jsonError != nil || errorDO.Code == "" || errorDO.Message == "" { + return defaultMessage + } + + formattedCodeMessage := fmt.Sprintf("%d: %s - %s", response.StatusCode, errorDO.Code, errorDO.Message) + + var formattedItems []string + for _, item := range errorDO.Errors { + if item.Message != "" && item.Property != "" { + formattedItems = append( + formattedItems, + fmt.Sprintf("%s: `%s`", item.Property, item.Message), + ) + } + } + + if len(formattedItems) > 0 { + return fmt.Sprintf("%s: %s", formattedCodeMessage, strings.Join(formattedItems, ", ")) + } + + return formattedCodeMessage +} + +func formatHTTPError(message string, statusCode int, body []byte) string { + if len(body) == 0 { + return fmt.Sprintf("%d: %s", statusCode, message) + } + return fmt.Sprintf("%d: %s - %s", statusCode, message, body) +} + +func handleHTTPError(response *http.Response, errorMessages ...map[int]string) string { + var body []byte + if response.Body != nil { + body, _ = io.ReadAll(response.Body) + response.Body = io.NopCloser(bytes.NewBuffer(body)) + } else { + body = make([]byte, 0) + } + for _, handler := range errorMessages { + for code, message := range handler { + if code == response.StatusCode { + return formatHTTPError( + message, + response.StatusCode, + body, + ) + } + + } + if defaultMessage, ok := handler[-1]; ok { + return formatHTTPError( + defaultMessage, + response.StatusCode, + body, + ) + } + + } + return formatHTTPError( + defaultUnknownErrorMessageConst, + response.StatusCode, + body, + ) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response_test.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response_test.go new file mode 100644 index 0000000..f94810e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/response_test.go @@ -0,0 +1,151 @@ +package yotierror + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +func TestError_ShouldReturnFormattedError(t *testing.T) { + jsonBytes, err := json.Marshal(DataObject{ + Code: "SOME_CODE", + Message: "some message", + Errors: []ItemDataObject{ + { + Message: "some property message", + Property: "some.property", + }, + }, + }) + assert.NilError(t, err) + + err = NewResponseError( + &http.Response{ + StatusCode: 401, + Body: io.NopCloser(bytes.NewReader(jsonBytes)), + }, + ) + + assert.ErrorContains(t, err, "SOME_CODE - some message: some.property: `some property message`") +} + +func TestError_ShouldReturnFormattedErrorCodeAndMessageOnly(t *testing.T) { + jsonBytes, err := json.Marshal(DataObject{ + Code: "SOME_CODE", + Message: "some message", + }) + assert.NilError(t, err) + + err = NewResponseError( + &http.Response{ + StatusCode: 400, + Body: io.NopCloser(bytes.NewReader(jsonBytes)), + }, + ) + + assert.ErrorContains(t, err, "400: SOME_CODE - some message") +} + +func TestError_ShouldReturnFormattedError_ReturnWrappedErrorByDefault(t *testing.T) { + err := NewResponseError( + &http.Response{ + StatusCode: 401, + }, + ) + + assert.ErrorContains(t, err, "401: unknown HTTP error") +} + +func TestError_ShouldReturnFormattedError_ReturnWrappedErrorWhenInvalidJSON(t *testing.T) { + response := &http.Response{ + StatusCode: 400, + Body: io.NopCloser(strings.NewReader("some invalid JSON")), + } + err := NewResponseError( + response, + ) + + assert.ErrorContains(t, err, "400: unknown HTTP error - some invalid JSON") + + errorResponse := err.Response + assert.Equal(t, response, errorResponse) + + body, readErr := io.ReadAll(errorResponse.Body) + assert.NilError(t, readErr) + + assert.Equal(t, string(body), "some invalid JSON") +} + +func TestError_ShouldReturnFormattedError_IgnoreUnknownErrorItems(t *testing.T) { + jsonString := "{\"message\": \"some message\", \"code\": \"SOME_CODE\", \"error\": [{\"some_key\": \"some value\"}]}" + response := &http.Response{ + StatusCode: 400, + Body: io.NopCloser(strings.NewReader(jsonString)), + } + err := NewResponseError( + response, + ) + + assert.ErrorContains(t, err, "400: SOME_CODE - some message") + + errorResponse := err.Response + assert.Equal(t, response, errorResponse) + + body, readErr := io.ReadAll(errorResponse.Body) + assert.NilError(t, readErr) + + assert.Equal(t, string(body), jsonString) +} + +func TestError_ShouldReturnCustomErrorForCode(t *testing.T) { + response := &http.Response{ + StatusCode: 404, + Body: io.NopCloser(strings.NewReader("some body")), + } + err := NewResponseError( + response, + map[int]string{404: "some message"}, + ) + + assert.ErrorContains(t, err, "404: some message - some body") +} + +func TestError_ShouldReturnCustomDefaultError(t *testing.T) { + response := &http.Response{ + StatusCode: 500, + Body: io.NopCloser(strings.NewReader("some body")), + } + err := NewResponseError( + response, + map[int]string{-1: "some default message"}, + ) + + assert.ErrorContains(t, err, "500: some default message - some body") +} + +func TestError_ShouldReturnTemporaryForServerError(t *testing.T) { + response := &http.Response{ + StatusCode: 500, + } + err := NewResponseError( + response, + ) + + assert.Check(t, err.Temporary()) +} + +func TestError_ShouldNotReturnTemporaryForClientError(t *testing.T) { + response := &http.Response{ + StatusCode: 400, + } + err := NewResponseError( + response, + ) + + assert.Check(t, !err.Temporary()) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/sharing_failure_error.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/sharing_failure_error.go new file mode 100644 index 0000000..07dfd11 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotierror/sharing_failure_error.go @@ -0,0 +1,10 @@ +package yotierror + +type DetailedSharingFailureError struct { + Code *string + Description *string +} + +func (d DetailedSharingFailureError) Error() string { + return "sharing failure" +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Attribute.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Attribute.pb.go new file mode 100644 index 0000000..b53c647 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Attribute.pb.go @@ -0,0 +1,670 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: Attribute.proto + +package yotiprotoattr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Attribute struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + ContentType ContentType `protobuf:"varint,3,opt,name=content_type,json=contentType,proto3,enum=attrpubapi_v1.ContentType" json:"content_type,omitempty"` + Anchors []*Anchor `protobuf:"bytes,4,rep,name=anchors,proto3" json:"anchors,omitempty"` + UserMetadata []*UserMetadata `protobuf:"bytes,5,rep,name=user_metadata,json=userMetadata,proto3" json:"user_metadata,omitempty"` + Metadata *Metadata `protobuf:"bytes,6,opt,name=metadata,proto3" json:"metadata,omitempty"` + EphemeralId string `protobuf:"bytes,7,opt,name=ephemeral_id,json=ephemeralId,proto3" json:"ephemeral_id,omitempty"` +} + +func (x *Attribute) Reset() { + *x = Attribute{} + if protoimpl.UnsafeEnabled { + mi := &file_Attribute_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Attribute) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Attribute) ProtoMessage() {} + +func (x *Attribute) ProtoReflect() protoreflect.Message { + mi := &file_Attribute_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Attribute.ProtoReflect.Descriptor instead. +func (*Attribute) Descriptor() ([]byte, []int) { + return file_Attribute_proto_rawDescGZIP(), []int{0} +} + +func (x *Attribute) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Attribute) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *Attribute) GetContentType() ContentType { + if x != nil { + return x.ContentType + } + return ContentType_UNDEFINED +} + +func (x *Attribute) GetAnchors() []*Anchor { + if x != nil { + return x.Anchors + } + return nil +} + +func (x *Attribute) GetUserMetadata() []*UserMetadata { + if x != nil { + return x.UserMetadata + } + return nil +} + +func (x *Attribute) GetMetadata() *Metadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *Attribute) GetEphemeralId() string { + if x != nil { + return x.EphemeralId + } + return "" +} + +type Metadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SupersededTimeStamp string `protobuf:"bytes,1,opt,name=superseded_time_stamp,json=supersededTimeStamp,proto3" json:"superseded_time_stamp,omitempty"` + Deletable bool `protobuf:"varint,2,opt,name=deletable,proto3" json:"deletable,omitempty"` + ReceiptId []byte `protobuf:"bytes,3,opt,name=receipt_id,json=receiptId,proto3" json:"receipt_id,omitempty"` + Revoked bool `protobuf:"varint,4,opt,name=revoked,proto3" json:"revoked,omitempty"` + Locked bool `protobuf:"varint,5,opt,name=locked,proto3" json:"locked,omitempty"` +} + +func (x *Metadata) Reset() { + *x = Metadata{} + if protoimpl.UnsafeEnabled { + mi := &file_Attribute_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Metadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Metadata) ProtoMessage() {} + +func (x *Metadata) ProtoReflect() protoreflect.Message { + mi := &file_Attribute_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Metadata.ProtoReflect.Descriptor instead. +func (*Metadata) Descriptor() ([]byte, []int) { + return file_Attribute_proto_rawDescGZIP(), []int{1} +} + +func (x *Metadata) GetSupersededTimeStamp() string { + if x != nil { + return x.SupersededTimeStamp + } + return "" +} + +func (x *Metadata) GetDeletable() bool { + if x != nil { + return x.Deletable + } + return false +} + +func (x *Metadata) GetReceiptId() []byte { + if x != nil { + return x.ReceiptId + } + return nil +} + +func (x *Metadata) GetRevoked() bool { + if x != nil { + return x.Revoked + } + return false +} + +func (x *Metadata) GetLocked() bool { + if x != nil { + return x.Locked + } + return false +} + +type Anchor struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ArtifactLink []byte `protobuf:"bytes,1,opt,name=artifact_link,json=artifactLink,proto3" json:"artifact_link,omitempty"` + OriginServerCerts [][]byte `protobuf:"bytes,2,rep,name=origin_server_certs,json=originServerCerts,proto3" json:"origin_server_certs,omitempty"` + ArtifactSignature []byte `protobuf:"bytes,3,opt,name=artifact_signature,json=artifactSignature,proto3" json:"artifact_signature,omitempty"` + SubType string `protobuf:"bytes,4,opt,name=sub_type,json=subType,proto3" json:"sub_type,omitempty"` + Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` + SignedTimeStamp []byte `protobuf:"bytes,6,opt,name=signed_time_stamp,json=signedTimeStamp,proto3" json:"signed_time_stamp,omitempty"` + AssociatedSource string `protobuf:"bytes,7,opt,name=associated_source,json=associatedSource,proto3" json:"associated_source,omitempty"` +} + +func (x *Anchor) Reset() { + *x = Anchor{} + if protoimpl.UnsafeEnabled { + mi := &file_Attribute_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Anchor) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Anchor) ProtoMessage() {} + +func (x *Anchor) ProtoReflect() protoreflect.Message { + mi := &file_Attribute_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Anchor.ProtoReflect.Descriptor instead. +func (*Anchor) Descriptor() ([]byte, []int) { + return file_Attribute_proto_rawDescGZIP(), []int{2} +} + +func (x *Anchor) GetArtifactLink() []byte { + if x != nil { + return x.ArtifactLink + } + return nil +} + +func (x *Anchor) GetOriginServerCerts() [][]byte { + if x != nil { + return x.OriginServerCerts + } + return nil +} + +func (x *Anchor) GetArtifactSignature() []byte { + if x != nil { + return x.ArtifactSignature + } + return nil +} + +func (x *Anchor) GetSubType() string { + if x != nil { + return x.SubType + } + return "" +} + +func (x *Anchor) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *Anchor) GetSignedTimeStamp() []byte { + if x != nil { + return x.SignedTimeStamp + } + return nil +} + +func (x *Anchor) GetAssociatedSource() string { + if x != nil { + return x.AssociatedSource + } + return "" +} + +type UserMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *UserMetadata) Reset() { + *x = UserMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_Attribute_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UserMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserMetadata) ProtoMessage() {} + +func (x *UserMetadata) ProtoReflect() protoreflect.Message { + mi := &file_Attribute_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserMetadata.ProtoReflect.Descriptor instead. +func (*UserMetadata) Descriptor() ([]byte, []int) { + return file_Attribute_proto_rawDescGZIP(), []int{3} +} + +func (x *UserMetadata) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *UserMetadata) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type MultiValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []*MultiValue_Value `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *MultiValue) Reset() { + *x = MultiValue{} + if protoimpl.UnsafeEnabled { + mi := &file_Attribute_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MultiValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MultiValue) ProtoMessage() {} + +func (x *MultiValue) ProtoReflect() protoreflect.Message { + mi := &file_Attribute_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MultiValue.ProtoReflect.Descriptor instead. +func (*MultiValue) Descriptor() ([]byte, []int) { + return file_Attribute_proto_rawDescGZIP(), []int{4} +} + +func (x *MultiValue) GetValues() []*MultiValue_Value { + if x != nil { + return x.Values + } + return nil +} + +type MultiValue_Value struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContentType ContentType `protobuf:"varint,1,opt,name=content_type,json=contentType,proto3,enum=attrpubapi_v1.ContentType" json:"content_type,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *MultiValue_Value) Reset() { + *x = MultiValue_Value{} + if protoimpl.UnsafeEnabled { + mi := &file_Attribute_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MultiValue_Value) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MultiValue_Value) ProtoMessage() {} + +func (x *MultiValue_Value) ProtoReflect() protoreflect.Message { + mi := &file_Attribute_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MultiValue_Value.ProtoReflect.Descriptor instead. +func (*MultiValue_Value) Descriptor() ([]byte, []int) { + return file_Attribute_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *MultiValue_Value) GetContentType() ContentType { + if x != nil { + return x.ContentType + } + return ContentType_UNDEFINED +} + +func (x *MultiValue_Value) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_Attribute_proto protoreflect.FileDescriptor + +var file_Attribute_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, + 0x1a, 0x11, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3d, 0x0a, 0x0c, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x61, 0x6e, + 0x63, 0x68, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x74, + 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x63, 0x68, + 0x6f, 0x72, 0x52, 0x07, 0x61, 0x6e, 0x63, 0x68, 0x6f, 0x72, 0x73, 0x12, 0x40, 0x0a, 0x0d, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, + 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x0c, 0x75, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, + 0x72, 0x61, 0x6c, 0x49, 0x64, 0x22, 0xad, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x75, 0x70, 0x65, 0x72, 0x73, 0x65, 0x64, 0x65, 0x64, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x13, 0x73, 0x75, 0x70, 0x65, 0x72, 0x73, 0x65, 0x64, 0x65, 0x64, 0x54, 0x69, 0x6d, + 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, + 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, + 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x22, 0x9e, 0x02, 0x0a, 0x06, 0x41, 0x6e, 0x63, 0x68, 0x6f, 0x72, + 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x6c, 0x69, 0x6e, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0c, 0x52, 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x43, 0x65, 0x72, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, + 0x74, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x11, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x2a, 0x0a, + 0x11, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x73, 0x73, + 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x36, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa1, + 0x01, 0x0a, 0x0a, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x37, 0x0a, + 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x4d, 0x75, + 0x6c, 0x74, 0x69, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0x5a, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, + 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x42, 0xe0, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x09, 0x41, 0x74, 0x74, + 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x2d, + 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x61, 0x74, 0x74, 0x72, 0xaa, 0x02, 0x1c, 0x59, 0x6f, 0x74, 0x69, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0xca, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, + 0x69, 0xe2, 0x02, 0x24, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x59, 0x6f, 0x74, 0x69, 0x3a, + 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x41, 0x74, 0x74, 0x72, 0x70, + 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_Attribute_proto_rawDescOnce sync.Once + file_Attribute_proto_rawDescData = file_Attribute_proto_rawDesc +) + +func file_Attribute_proto_rawDescGZIP() []byte { + file_Attribute_proto_rawDescOnce.Do(func() { + file_Attribute_proto_rawDescData = protoimpl.X.CompressGZIP(file_Attribute_proto_rawDescData) + }) + return file_Attribute_proto_rawDescData +} + +var file_Attribute_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_Attribute_proto_goTypes = []interface{}{ + (*Attribute)(nil), // 0: attrpubapi_v1.Attribute + (*Metadata)(nil), // 1: attrpubapi_v1.Metadata + (*Anchor)(nil), // 2: attrpubapi_v1.Anchor + (*UserMetadata)(nil), // 3: attrpubapi_v1.UserMetadata + (*MultiValue)(nil), // 4: attrpubapi_v1.MultiValue + (*MultiValue_Value)(nil), // 5: attrpubapi_v1.MultiValue.Value + (ContentType)(0), // 6: attrpubapi_v1.ContentType +} +var file_Attribute_proto_depIdxs = []int32{ + 6, // 0: attrpubapi_v1.Attribute.content_type:type_name -> attrpubapi_v1.ContentType + 2, // 1: attrpubapi_v1.Attribute.anchors:type_name -> attrpubapi_v1.Anchor + 3, // 2: attrpubapi_v1.Attribute.user_metadata:type_name -> attrpubapi_v1.UserMetadata + 1, // 3: attrpubapi_v1.Attribute.metadata:type_name -> attrpubapi_v1.Metadata + 5, // 4: attrpubapi_v1.MultiValue.values:type_name -> attrpubapi_v1.MultiValue.Value + 6, // 5: attrpubapi_v1.MultiValue.Value.content_type:type_name -> attrpubapi_v1.ContentType + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_Attribute_proto_init() } +func file_Attribute_proto_init() { + if File_Attribute_proto != nil { + return + } + file_ContentType_proto_init() + if !protoimpl.UnsafeEnabled { + file_Attribute_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Attribute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Attribute_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Metadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Attribute_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Anchor); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Attribute_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UserMetadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Attribute_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MultiValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Attribute_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MultiValue_Value); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Attribute_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Attribute_proto_goTypes, + DependencyIndexes: file_Attribute_proto_depIdxs, + MessageInfos: file_Attribute_proto_msgTypes, + }.Build() + File_Attribute_proto = out.File + file_Attribute_proto_rawDesc = nil + file_Attribute_proto_goTypes = nil + file_Attribute_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/ContentType.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/ContentType.pb.go new file mode 100644 index 0000000..2fbf1e9 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/ContentType.pb.go @@ -0,0 +1,164 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: ContentType.proto + +package yotiprotoattr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ContentType int32 + +const ( + ContentType_UNDEFINED ContentType = 0 + ContentType_STRING ContentType = 1 + ContentType_JPEG ContentType = 2 + ContentType_DATE ContentType = 3 + ContentType_PNG ContentType = 4 + ContentType_JSON ContentType = 5 + ContentType_MULTI_VALUE ContentType = 6 + ContentType_INT ContentType = 7 +) + +// Enum value maps for ContentType. +var ( + ContentType_name = map[int32]string{ + 0: "UNDEFINED", + 1: "STRING", + 2: "JPEG", + 3: "DATE", + 4: "PNG", + 5: "JSON", + 6: "MULTI_VALUE", + 7: "INT", + } + ContentType_value = map[string]int32{ + "UNDEFINED": 0, + "STRING": 1, + "JPEG": 2, + "DATE": 3, + "PNG": 4, + "JSON": 5, + "MULTI_VALUE": 6, + "INT": 7, + } +) + +func (x ContentType) Enum() *ContentType { + p := new(ContentType) + *p = x + return p +} + +func (x ContentType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ContentType) Descriptor() protoreflect.EnumDescriptor { + return file_ContentType_proto_enumTypes[0].Descriptor() +} + +func (ContentType) Type() protoreflect.EnumType { + return &file_ContentType_proto_enumTypes[0] +} + +func (x ContentType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ContentType.Descriptor instead. +func (ContentType) EnumDescriptor() ([]byte, []int) { + return file_ContentType_proto_rawDescGZIP(), []int{0} +} + +var File_ContentType_proto protoreflect.FileDescriptor + +var file_ContentType_proto_rawDesc = []byte{ + 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, + 0x76, 0x31, 0x2a, 0x69, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, + 0x4a, 0x50, 0x45, 0x47, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, + 0x12, 0x07, 0x0a, 0x03, 0x50, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x4a, 0x53, 0x4f, + 0x4e, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x5f, 0x56, 0x41, 0x4c, + 0x55, 0x45, 0x10, 0x06, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x07, 0x42, 0xe7, 0x01, + 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, + 0x69, 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x74, 0x74, 0x72, 0xaa, 0x02, 0x1c, 0x59, 0x6f, 0x74, 0x69, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0xca, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, 0x5c, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, + 0x61, 0x70, 0x69, 0xe2, 0x02, 0x24, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, + 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x59, 0x6f, 0x74, + 0x69, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x41, 0x74, 0x74, + 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ContentType_proto_rawDescOnce sync.Once + file_ContentType_proto_rawDescData = file_ContentType_proto_rawDesc +) + +func file_ContentType_proto_rawDescGZIP() []byte { + file_ContentType_proto_rawDescOnce.Do(func() { + file_ContentType_proto_rawDescData = protoimpl.X.CompressGZIP(file_ContentType_proto_rawDescData) + }) + return file_ContentType_proto_rawDescData +} + +var file_ContentType_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_ContentType_proto_goTypes = []interface{}{ + (ContentType)(0), // 0: attrpubapi_v1.ContentType +} +var file_ContentType_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_ContentType_proto_init() } +func file_ContentType_proto_init() { + if File_ContentType_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ContentType_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ContentType_proto_goTypes, + DependencyIndexes: file_ContentType_proto_depIdxs, + EnumInfos: file_ContentType_proto_enumTypes, + }.Build() + File_ContentType_proto = out.File + file_ContentType_proto_rawDesc = nil + file_ContentType_proto_goTypes = nil + file_ContentType_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/List.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/List.pb.go new file mode 100644 index 0000000..ec77e02 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/List.pb.go @@ -0,0 +1,306 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: List.proto + +package yotiprotoattr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type AttributeAndId struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Attribute *Attribute `protobuf:"bytes,1,opt,name=attribute,proto3" json:"attribute,omitempty"` + AttributeId []byte `protobuf:"bytes,2,opt,name=attribute_id,json=attributeId,proto3" json:"attribute_id,omitempty"` +} + +func (x *AttributeAndId) Reset() { + *x = AttributeAndId{} + if protoimpl.UnsafeEnabled { + mi := &file_List_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttributeAndId) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttributeAndId) ProtoMessage() {} + +func (x *AttributeAndId) ProtoReflect() protoreflect.Message { + mi := &file_List_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttributeAndId.ProtoReflect.Descriptor instead. +func (*AttributeAndId) Descriptor() ([]byte, []int) { + return file_List_proto_rawDescGZIP(), []int{0} +} + +func (x *AttributeAndId) GetAttribute() *Attribute { + if x != nil { + return x.Attribute + } + return nil +} + +func (x *AttributeAndId) GetAttributeId() []byte { + if x != nil { + return x.AttributeId + } + return nil +} + +type AttributeAndIdList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AttributeAndIdList []*AttributeAndId `protobuf:"bytes,1,rep,name=attribute_and_id_list,json=attributeAndIdList,proto3" json:"attribute_and_id_list,omitempty"` +} + +func (x *AttributeAndIdList) Reset() { + *x = AttributeAndIdList{} + if protoimpl.UnsafeEnabled { + mi := &file_List_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttributeAndIdList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttributeAndIdList) ProtoMessage() {} + +func (x *AttributeAndIdList) ProtoReflect() protoreflect.Message { + mi := &file_List_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttributeAndIdList.ProtoReflect.Descriptor instead. +func (*AttributeAndIdList) Descriptor() ([]byte, []int) { + return file_List_proto_rawDescGZIP(), []int{1} +} + +func (x *AttributeAndIdList) GetAttributeAndIdList() []*AttributeAndId { + if x != nil { + return x.AttributeAndIdList + } + return nil +} + +type AttributeList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Attributes []*Attribute `protobuf:"bytes,1,rep,name=attributes,proto3" json:"attributes,omitempty"` +} + +func (x *AttributeList) Reset() { + *x = AttributeList{} + if protoimpl.UnsafeEnabled { + mi := &file_List_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttributeList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttributeList) ProtoMessage() {} + +func (x *AttributeList) ProtoReflect() protoreflect.Message { + mi := &file_List_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttributeList.ProtoReflect.Descriptor instead. +func (*AttributeList) Descriptor() ([]byte, []int) { + return file_List_proto_rawDescGZIP(), []int{2} +} + +func (x *AttributeList) GetAttributes() []*Attribute { + if x != nil { + return x.Attributes + } + return nil +} + +var File_List_proto protoreflect.FileDescriptor + +var file_List_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x61, 0x74, + 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x1a, 0x0f, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6b, 0x0a, 0x0e, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x49, 0x64, 0x12, 0x36, + 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, + 0x31, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09, 0x61, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x61, 0x74, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x64, 0x22, 0x66, 0x0a, 0x12, 0x41, 0x74, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x49, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x12, + 0x50, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, + 0x5f, 0x69, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x41, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x49, 0x64, 0x52, 0x12, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x49, 0x64, 0x4c, 0x69, 0x73, + 0x74, 0x22, 0x49, 0x0a, 0x0d, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, + 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0xe9, 0x01, 0x0a, + 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x12, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, + 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, + 0x74, 0x69, 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, + 0x69, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x74, 0x74, 0x72, 0xaa, 0x02, 0x1c, 0x59, 0x6f, 0x74, + 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0xca, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, + 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, + 0x62, 0x61, 0x70, 0x69, 0xe2, 0x02, 0x24, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x59, 0x6f, + 0x74, 0x69, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x41, 0x74, + 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_List_proto_rawDescOnce sync.Once + file_List_proto_rawDescData = file_List_proto_rawDesc +) + +func file_List_proto_rawDescGZIP() []byte { + file_List_proto_rawDescOnce.Do(func() { + file_List_proto_rawDescData = protoimpl.X.CompressGZIP(file_List_proto_rawDescData) + }) + return file_List_proto_rawDescData +} + +var file_List_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_List_proto_goTypes = []interface{}{ + (*AttributeAndId)(nil), // 0: attrpubapi_v1.AttributeAndId + (*AttributeAndIdList)(nil), // 1: attrpubapi_v1.AttributeAndIdList + (*AttributeList)(nil), // 2: attrpubapi_v1.AttributeList + (*Attribute)(nil), // 3: attrpubapi_v1.Attribute +} +var file_List_proto_depIdxs = []int32{ + 3, // 0: attrpubapi_v1.AttributeAndId.attribute:type_name -> attrpubapi_v1.Attribute + 0, // 1: attrpubapi_v1.AttributeAndIdList.attribute_and_id_list:type_name -> attrpubapi_v1.AttributeAndId + 3, // 2: attrpubapi_v1.AttributeList.attributes:type_name -> attrpubapi_v1.Attribute + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_List_proto_init() } +func file_List_proto_init() { + if File_List_proto != nil { + return + } + file_Attribute_proto_init() + if !protoimpl.UnsafeEnabled { + file_List_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributeAndId); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_List_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributeAndIdList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_List_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributeList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_List_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_List_proto_goTypes, + DependencyIndexes: file_List_proto_depIdxs, + MessageInfos: file_List_proto_msgTypes, + }.Build() + File_List_proto = out.File + file_List_proto_rawDesc = nil + file_List_proto_goTypes = nil + file_List_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Signing.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Signing.pb.go new file mode 100644 index 0000000..91e8609 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoattr/Signing.pb.go @@ -0,0 +1,224 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: Signing.proto + +package yotiprotoattr + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type AttributeSigning struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + ContentType ContentType `protobuf:"varint,3,opt,name=content_type,json=contentType,proto3,enum=attrpubapi_v1.ContentType" json:"content_type,omitempty"` + ArtifactSignature []byte `protobuf:"bytes,4,opt,name=artifact_signature,json=artifactSignature,proto3" json:"artifact_signature,omitempty"` + SubType string `protobuf:"bytes,5,opt,name=sub_type,json=subType,proto3" json:"sub_type,omitempty"` + SignedTimeStamp []byte `protobuf:"bytes,6,opt,name=signed_time_stamp,json=signedTimeStamp,proto3" json:"signed_time_stamp,omitempty"` + AssociatedSource string `protobuf:"bytes,7,opt,name=associated_source,json=associatedSource,proto3" json:"associated_source,omitempty"` +} + +func (x *AttributeSigning) Reset() { + *x = AttributeSigning{} + if protoimpl.UnsafeEnabled { + mi := &file_Signing_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AttributeSigning) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AttributeSigning) ProtoMessage() {} + +func (x *AttributeSigning) ProtoReflect() protoreflect.Message { + mi := &file_Signing_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AttributeSigning.ProtoReflect.Descriptor instead. +func (*AttributeSigning) Descriptor() ([]byte, []int) { + return file_Signing_proto_rawDescGZIP(), []int{0} +} + +func (x *AttributeSigning) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AttributeSigning) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *AttributeSigning) GetContentType() ContentType { + if x != nil { + return x.ContentType + } + return ContentType_UNDEFINED +} + +func (x *AttributeSigning) GetArtifactSignature() []byte { + if x != nil { + return x.ArtifactSignature + } + return nil +} + +func (x *AttributeSigning) GetSubType() string { + if x != nil { + return x.SubType + } + return "" +} + +func (x *AttributeSigning) GetSignedTimeStamp() []byte { + if x != nil { + return x.SignedTimeStamp + } + return nil +} + +func (x *AttributeSigning) GetAssociatedSource() string { + if x != nil { + return x.AssociatedSource + } + return "" +} + +var File_Signing_proto protoreflect.FileDescriptor + +var file_Signing_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0d, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x1a, 0x11, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x9e, 0x02, 0x0a, 0x10, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x53, + 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x3d, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, + 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x2d, 0x0a, 0x12, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x61, 0x72, 0x74, + 0x69, 0x66, 0x61, 0x63, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x73, 0x75, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, + 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x42, 0xe3, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x0c, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, + 0x74, 0x69, 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, + 0x69, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x74, 0x74, 0x72, 0xaa, 0x02, 0x1c, 0x59, 0x6f, 0x74, + 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, + 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0xca, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, + 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, + 0x62, 0x61, 0x70, 0x69, 0xe2, 0x02, 0x24, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x41, 0x74, 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, + 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1a, 0x59, 0x6f, + 0x74, 0x69, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x41, 0x74, + 0x74, 0x72, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_Signing_proto_rawDescOnce sync.Once + file_Signing_proto_rawDescData = file_Signing_proto_rawDesc +) + +func file_Signing_proto_rawDescGZIP() []byte { + file_Signing_proto_rawDescOnce.Do(func() { + file_Signing_proto_rawDescData = protoimpl.X.CompressGZIP(file_Signing_proto_rawDescData) + }) + return file_Signing_proto_rawDescData +} + +var file_Signing_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_Signing_proto_goTypes = []interface{}{ + (*AttributeSigning)(nil), // 0: attrpubapi_v1.AttributeSigning + (ContentType)(0), // 1: attrpubapi_v1.ContentType +} +var file_Signing_proto_depIdxs = []int32{ + 1, // 0: attrpubapi_v1.AttributeSigning.content_type:type_name -> attrpubapi_v1.ContentType + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_Signing_proto_init() } +func file_Signing_proto_init() { + if File_Signing_proto != nil { + return + } + file_ContentType_proto_init() + if !protoimpl.UnsafeEnabled { + file_Signing_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AttributeSigning); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Signing_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Signing_proto_goTypes, + DependencyIndexes: file_Signing_proto_depIdxs, + MessageInfos: file_Signing_proto_msgTypes, + }.Build() + File_Signing_proto = out.File + file_Signing_proto_rawDesc = nil + file_Signing_proto_goTypes = nil + file_Signing_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/EncryptedData.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/EncryptedData.pb.go new file mode 100644 index 0000000..9bb7f54 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/EncryptedData.pb.go @@ -0,0 +1,167 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: EncryptedData.proto + +package yotiprotocom + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EncryptedData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Iv []byte `protobuf:"bytes,1,opt,name=iv,proto3" json:"iv,omitempty"` + CipherText []byte `protobuf:"bytes,2,opt,name=cipher_text,json=cipherText,proto3" json:"cipher_text,omitempty"` +} + +func (x *EncryptedData) Reset() { + *x = EncryptedData{} + if protoimpl.UnsafeEnabled { + mi := &file_EncryptedData_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EncryptedData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EncryptedData) ProtoMessage() {} + +func (x *EncryptedData) ProtoReflect() protoreflect.Message { + mi := &file_EncryptedData_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EncryptedData.ProtoReflect.Descriptor instead. +func (*EncryptedData) Descriptor() ([]byte, []int) { + return file_EncryptedData_proto_rawDescGZIP(), []int{0} +} + +func (x *EncryptedData) GetIv() []byte { + if x != nil { + return x.Iv + } + return nil +} + +func (x *EncryptedData) GetCipherText() []byte { + if x != nil { + return x.CipherText + } + return nil +} + +var File_EncryptedData_proto protoreflect.FileDescriptor + +var file_EncryptedData_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, + 0x5f, 0x76, 0x31, 0x22, 0x40, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x76, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x02, 0x69, 0x76, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x54, 0x65, 0x78, 0x74, 0x42, 0xe2, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, + 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x12, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, + 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x2d, 0x67, 0x6f, 0x2d, 0x73, + 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0xaa, 0x02, 0x19, 0x59, 0x6f, 0x74, 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0xca, 0x02, + 0x17, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x43, + 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0xe2, 0x02, 0x23, 0x59, 0x6f, 0x74, 0x69, 0x5c, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, + 0x70, 0x69, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x19, 0x59, 0x6f, 0x74, 0x69, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, + 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_EncryptedData_proto_rawDescOnce sync.Once + file_EncryptedData_proto_rawDescData = file_EncryptedData_proto_rawDesc +) + +func file_EncryptedData_proto_rawDescGZIP() []byte { + file_EncryptedData_proto_rawDescOnce.Do(func() { + file_EncryptedData_proto_rawDescData = protoimpl.X.CompressGZIP(file_EncryptedData_proto_rawDescData) + }) + return file_EncryptedData_proto_rawDescData +} + +var file_EncryptedData_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_EncryptedData_proto_goTypes = []interface{}{ + (*EncryptedData)(nil), // 0: compubapi_v1.EncryptedData +} +var file_EncryptedData_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_EncryptedData_proto_init() } +func file_EncryptedData_proto_init() { + if File_EncryptedData_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_EncryptedData_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptedData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_EncryptedData_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_EncryptedData_proto_goTypes, + DependencyIndexes: file_EncryptedData_proto_depIdxs, + MessageInfos: file_EncryptedData_proto_msgTypes, + }.Build() + File_EncryptedData_proto = out.File + file_EncryptedData_proto_rawDesc = nil + file_EncryptedData_proto_goTypes = nil + file_EncryptedData_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/SignedTimestamp.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/SignedTimestamp.pb.go new file mode 100644 index 0000000..3f0b13d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotocom/SignedTimestamp.pb.go @@ -0,0 +1,210 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: SignedTimestamp.proto + +package yotiprotocom + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SignedTimestamp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + MessageDigest []byte `protobuf:"bytes,3,opt,name=message_digest,json=messageDigest,proto3" json:"message_digest,omitempty"` + ChainDigest []byte `protobuf:"bytes,4,opt,name=chain_digest,json=chainDigest,proto3" json:"chain_digest,omitempty"` + ChainDigestSkip1 []byte `protobuf:"bytes,5,opt,name=chain_digest_skip1,json=chainDigestSkip1,proto3" json:"chain_digest_skip1,omitempty"` + ChainDigestSkip2 []byte `protobuf:"bytes,6,opt,name=chain_digest_skip2,json=chainDigestSkip2,proto3" json:"chain_digest_skip2,omitempty"` +} + +func (x *SignedTimestamp) Reset() { + *x = SignedTimestamp{} + if protoimpl.UnsafeEnabled { + mi := &file_SignedTimestamp_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignedTimestamp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignedTimestamp) ProtoMessage() {} + +func (x *SignedTimestamp) ProtoReflect() protoreflect.Message { + mi := &file_SignedTimestamp_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignedTimestamp.ProtoReflect.Descriptor instead. +func (*SignedTimestamp) Descriptor() ([]byte, []int) { + return file_SignedTimestamp_proto_rawDescGZIP(), []int{0} +} + +func (x *SignedTimestamp) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *SignedTimestamp) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *SignedTimestamp) GetMessageDigest() []byte { + if x != nil { + return x.MessageDigest + } + return nil +} + +func (x *SignedTimestamp) GetChainDigest() []byte { + if x != nil { + return x.ChainDigest + } + return nil +} + +func (x *SignedTimestamp) GetChainDigestSkip1() []byte { + if x != nil { + return x.ChainDigestSkip1 + } + return nil +} + +func (x *SignedTimestamp) GetChainDigestSkip2() []byte { + if x != nil { + return x.ChainDigestSkip2 + } + return nil +} + +var File_SignedTimestamp_proto protoreflect.FileDescriptor + +var file_SignedTimestamp_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, + 0x70, 0x69, 0x5f, 0x76, 0x31, 0x22, 0xef, 0x01, 0x0a, 0x0f, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6b, 0x69, 0x70, + 0x31, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x70, 0x31, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x32, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x44, 0x69, 0x67, 0x65, + 0x73, 0x74, 0x53, 0x6b, 0x69, 0x70, 0x32, 0x42, 0xe4, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, + 0x79, 0x6f, 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, + 0x73, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x42, 0x14, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x2d, + 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0xaa, 0x02, 0x19, 0x59, 0x6f, 0x74, 0x69, 0x2e, 0x41, 0x75, + 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0xca, 0x02, 0x17, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x5c, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0xe2, 0x02, 0x23, 0x59, + 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x43, 0x6f, 0x6d, + 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0xea, 0x02, 0x19, 0x59, 0x6f, 0x74, 0x69, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_SignedTimestamp_proto_rawDescOnce sync.Once + file_SignedTimestamp_proto_rawDescData = file_SignedTimestamp_proto_rawDesc +) + +func file_SignedTimestamp_proto_rawDescGZIP() []byte { + file_SignedTimestamp_proto_rawDescOnce.Do(func() { + file_SignedTimestamp_proto_rawDescData = protoimpl.X.CompressGZIP(file_SignedTimestamp_proto_rawDescData) + }) + return file_SignedTimestamp_proto_rawDescData +} + +var file_SignedTimestamp_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_SignedTimestamp_proto_goTypes = []interface{}{ + (*SignedTimestamp)(nil), // 0: compubapi_v1.SignedTimestamp +} +var file_SignedTimestamp_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_SignedTimestamp_proto_init() } +func file_SignedTimestamp_proto_init() { + if File_SignedTimestamp_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_SignedTimestamp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignedTimestamp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_SignedTimestamp_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_SignedTimestamp_proto_goTypes, + DependencyIndexes: file_SignedTimestamp_proto_depIdxs, + MessageInfos: file_SignedTimestamp_proto_msgTypes, + }.Build() + File_SignedTimestamp_proto = out.File + file_SignedTimestamp_proto_rawDesc = nil + file_SignedTimestamp_proto_goTypes = nil + file_SignedTimestamp_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/DataEntry.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/DataEntry.pb.go new file mode 100644 index 0000000..add01e4 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/DataEntry.pb.go @@ -0,0 +1,242 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: DataEntry.proto + +package yotiprotoshare + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type DataEntry_Type int32 + +const ( + DataEntry_UNDEFINED DataEntry_Type = 0 + DataEntry_INVOICE DataEntry_Type = 1 + DataEntry_PAYMENT_TRANSACTION DataEntry_Type = 2 + DataEntry_LOCATION DataEntry_Type = 3 + DataEntry_TRANSACTION DataEntry_Type = 4 + DataEntry_AGE_VERIFICATION_SECRET DataEntry_Type = 5 + DataEntry_THIRD_PARTY_ATTRIBUTE DataEntry_Type = 6 +) + +// Enum value maps for DataEntry_Type. +var ( + DataEntry_Type_name = map[int32]string{ + 0: "UNDEFINED", + 1: "INVOICE", + 2: "PAYMENT_TRANSACTION", + 3: "LOCATION", + 4: "TRANSACTION", + 5: "AGE_VERIFICATION_SECRET", + 6: "THIRD_PARTY_ATTRIBUTE", + } + DataEntry_Type_value = map[string]int32{ + "UNDEFINED": 0, + "INVOICE": 1, + "PAYMENT_TRANSACTION": 2, + "LOCATION": 3, + "TRANSACTION": 4, + "AGE_VERIFICATION_SECRET": 5, + "THIRD_PARTY_ATTRIBUTE": 6, + } +) + +func (x DataEntry_Type) Enum() *DataEntry_Type { + p := new(DataEntry_Type) + *p = x + return p +} + +func (x DataEntry_Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DataEntry_Type) Descriptor() protoreflect.EnumDescriptor { + return file_DataEntry_proto_enumTypes[0].Descriptor() +} + +func (DataEntry_Type) Type() protoreflect.EnumType { + return &file_DataEntry_proto_enumTypes[0] +} + +func (x DataEntry_Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DataEntry_Type.Descriptor instead. +func (DataEntry_Type) EnumDescriptor() ([]byte, []int) { + return file_DataEntry_proto_rawDescGZIP(), []int{0, 0} +} + +type DataEntry struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type DataEntry_Type `protobuf:"varint,1,opt,name=type,proto3,enum=sharepubapi_v1.DataEntry_Type" json:"type,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *DataEntry) Reset() { + *x = DataEntry{} + if protoimpl.UnsafeEnabled { + mi := &file_DataEntry_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DataEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DataEntry) ProtoMessage() {} + +func (x *DataEntry) ProtoReflect() protoreflect.Message { + mi := &file_DataEntry_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DataEntry.ProtoReflect.Descriptor instead. +func (*DataEntry) Descriptor() ([]byte, []int) { + return file_DataEntry_proto_rawDescGZIP(), []int{0} +} + +func (x *DataEntry) GetType() DataEntry_Type { + if x != nil { + return x.Type + } + return DataEntry_UNDEFINED +} + +func (x *DataEntry) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +var File_DataEntry_proto protoreflect.FileDescriptor + +var file_DataEntry_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x0e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, + 0x31, 0x22, 0xea, 0x01, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x04, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x01, 0x12, 0x17, + 0x0a, 0x13, 0x50, 0x41, 0x59, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x43, 0x41, 0x54, + 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, + 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x41, 0x47, 0x45, 0x5f, 0x56, 0x45, + 0x52, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x45, 0x43, 0x52, 0x45, + 0x54, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x48, 0x49, 0x52, 0x44, 0x5f, 0x50, 0x41, 0x52, + 0x54, 0x59, 0x5f, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, 0x10, 0x06, 0x42, 0xe5, + 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x0e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, 0x69, + 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x61, 0x72, 0x65, 0xaa, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x53, + 0x68, 0x61, 0x72, 0x65, 0xca, 0x02, 0x19, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, + 0xe2, 0x02, 0x25, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1b, 0x59, 0x6f, 0x74, 0x69, 0x3a, + 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x53, 0x68, 0x61, 0x72, 0x65, + 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_DataEntry_proto_rawDescOnce sync.Once + file_DataEntry_proto_rawDescData = file_DataEntry_proto_rawDesc +) + +func file_DataEntry_proto_rawDescGZIP() []byte { + file_DataEntry_proto_rawDescOnce.Do(func() { + file_DataEntry_proto_rawDescData = protoimpl.X.CompressGZIP(file_DataEntry_proto_rawDescData) + }) + return file_DataEntry_proto_rawDescData +} + +var file_DataEntry_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_DataEntry_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_DataEntry_proto_goTypes = []interface{}{ + (DataEntry_Type)(0), // 0: sharepubapi_v1.DataEntry.Type + (*DataEntry)(nil), // 1: sharepubapi_v1.DataEntry +} +var file_DataEntry_proto_depIdxs = []int32{ + 0, // 0: sharepubapi_v1.DataEntry.type:type_name -> sharepubapi_v1.DataEntry.Type + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_DataEntry_proto_init() } +func file_DataEntry_proto_init() { + if File_DataEntry_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_DataEntry_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DataEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_DataEntry_proto_rawDesc, + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_DataEntry_proto_goTypes, + DependencyIndexes: file_DataEntry_proto_depIdxs, + EnumInfos: file_DataEntry_proto_enumTypes, + MessageInfos: file_DataEntry_proto_msgTypes, + }.Build() + File_DataEntry_proto = out.File + file_DataEntry_proto_rawDesc = nil + file_DataEntry_proto_goTypes = nil + file_DataEntry_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ExtraData.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ExtraData.pb.go new file mode 100644 index 0000000..88e555f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ExtraData.pb.go @@ -0,0 +1,162 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: ExtraData.proto + +package yotiprotoshare + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ExtraData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + List []*DataEntry `protobuf:"bytes,1,rep,name=list,proto3" json:"list,omitempty"` +} + +func (x *ExtraData) Reset() { + *x = ExtraData{} + if protoimpl.UnsafeEnabled { + mi := &file_ExtraData_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExtraData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExtraData) ProtoMessage() {} + +func (x *ExtraData) ProtoReflect() protoreflect.Message { + mi := &file_ExtraData_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExtraData.ProtoReflect.Descriptor instead. +func (*ExtraData) Descriptor() ([]byte, []int) { + return file_ExtraData_proto_rawDescGZIP(), []int{0} +} + +func (x *ExtraData) GetList() []*DataEntry { + if x != nil { + return x.List + } + return nil +} + +var File_ExtraData_proto protoreflect.FileDescriptor + +var file_ExtraData_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x0e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, + 0x31, 0x1a, 0x0f, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x09, 0x45, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x2d, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x73, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x42, 0xe5, + 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x0e, 0x45, 0x78, 0x74, 0x72, 0x61, 0x44, 0x61, + 0x74, 0x61, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, 0x69, + 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x61, 0x72, 0x65, 0xaa, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x53, + 0x68, 0x61, 0x72, 0x65, 0xca, 0x02, 0x19, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, + 0xe2, 0x02, 0x25, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1b, 0x59, 0x6f, 0x74, 0x69, 0x3a, + 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x53, 0x68, 0x61, 0x72, 0x65, + 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ExtraData_proto_rawDescOnce sync.Once + file_ExtraData_proto_rawDescData = file_ExtraData_proto_rawDesc +) + +func file_ExtraData_proto_rawDescGZIP() []byte { + file_ExtraData_proto_rawDescOnce.Do(func() { + file_ExtraData_proto_rawDescData = protoimpl.X.CompressGZIP(file_ExtraData_proto_rawDescData) + }) + return file_ExtraData_proto_rawDescData +} + +var file_ExtraData_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_ExtraData_proto_goTypes = []interface{}{ + (*ExtraData)(nil), // 0: sharepubapi_v1.ExtraData + (*DataEntry)(nil), // 1: sharepubapi_v1.DataEntry +} +var file_ExtraData_proto_depIdxs = []int32{ + 1, // 0: sharepubapi_v1.ExtraData.list:type_name -> sharepubapi_v1.DataEntry + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ExtraData_proto_init() } +func file_ExtraData_proto_init() { + if File_ExtraData_proto != nil { + return + } + file_DataEntry_proto_init() + if !protoimpl.UnsafeEnabled { + file_ExtraData_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExtraData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ExtraData_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ExtraData_proto_goTypes, + DependencyIndexes: file_ExtraData_proto_depIdxs, + MessageInfos: file_ExtraData_proto_msgTypes, + }.Build() + File_ExtraData_proto = out.File + file_ExtraData_proto_rawDesc = nil + file_ExtraData_proto_goTypes = nil + file_ExtraData_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/IssuingAttributes.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/IssuingAttributes.pb.go new file mode 100644 index 0000000..758fb28 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/IssuingAttributes.pb.go @@ -0,0 +1,234 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: IssuingAttributes.proto + +package yotiprotoshare + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type IssuingAttributes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ExpiryDate string `protobuf:"bytes,1,opt,name=expiry_date,json=expiryDate,proto3" json:"expiry_date,omitempty"` + Definitions []*Definition `protobuf:"bytes,2,rep,name=definitions,proto3" json:"definitions,omitempty"` +} + +func (x *IssuingAttributes) Reset() { + *x = IssuingAttributes{} + if protoimpl.UnsafeEnabled { + mi := &file_IssuingAttributes_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IssuingAttributes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IssuingAttributes) ProtoMessage() {} + +func (x *IssuingAttributes) ProtoReflect() protoreflect.Message { + mi := &file_IssuingAttributes_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IssuingAttributes.ProtoReflect.Descriptor instead. +func (*IssuingAttributes) Descriptor() ([]byte, []int) { + return file_IssuingAttributes_proto_rawDescGZIP(), []int{0} +} + +func (x *IssuingAttributes) GetExpiryDate() string { + if x != nil { + return x.ExpiryDate + } + return "" +} + +func (x *IssuingAttributes) GetDefinitions() []*Definition { + if x != nil { + return x.Definitions + } + return nil +} + +type Definition struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Definition) Reset() { + *x = Definition{} + if protoimpl.UnsafeEnabled { + mi := &file_IssuingAttributes_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Definition) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Definition) ProtoMessage() {} + +func (x *Definition) ProtoReflect() protoreflect.Message { + mi := &file_IssuingAttributes_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Definition.ProtoReflect.Descriptor instead. +func (*Definition) Descriptor() ([]byte, []int) { + return file_IssuingAttributes_proto_rawDescGZIP(), []int{1} +} + +func (x *Definition) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_IssuingAttributes_proto protoreflect.FileDescriptor + +var file_IssuingAttributes_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x49, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x73, 0x68, 0x61, 0x72, 0x65, + 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x22, 0x72, 0x0a, 0x11, 0x49, 0x73, 0x73, + 0x75, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1f, + 0x0a, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x44, 0x61, 0x74, 0x65, 0x12, + 0x3c, 0x0a, 0x0b, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, + 0x70, 0x69, 0x5f, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0b, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x20, 0x0a, + 0x0a, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, + 0xed, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x16, 0x49, 0x73, 0x73, 0x75, 0x69, 0x6e, + 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, + 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, + 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x61, + 0x72, 0x65, 0xaa, 0x02, 0x18, 0x59, 0x6f, 0x74, 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, 0x53, 0x68, 0x61, 0x72, 0x65, 0xca, 0x02, 0x19, + 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x53, 0x68, + 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0xe2, 0x02, 0x25, 0x59, 0x6f, 0x74, 0x69, + 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, + 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0xea, 0x02, 0x1b, 0x59, 0x6f, 0x74, 0x69, 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x3a, 0x3a, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_IssuingAttributes_proto_rawDescOnce sync.Once + file_IssuingAttributes_proto_rawDescData = file_IssuingAttributes_proto_rawDesc +) + +func file_IssuingAttributes_proto_rawDescGZIP() []byte { + file_IssuingAttributes_proto_rawDescOnce.Do(func() { + file_IssuingAttributes_proto_rawDescData = protoimpl.X.CompressGZIP(file_IssuingAttributes_proto_rawDescData) + }) + return file_IssuingAttributes_proto_rawDescData +} + +var file_IssuingAttributes_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_IssuingAttributes_proto_goTypes = []interface{}{ + (*IssuingAttributes)(nil), // 0: sharepubapi_v1.IssuingAttributes + (*Definition)(nil), // 1: sharepubapi_v1.Definition +} +var file_IssuingAttributes_proto_depIdxs = []int32{ + 1, // 0: sharepubapi_v1.IssuingAttributes.definitions:type_name -> sharepubapi_v1.Definition + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_IssuingAttributes_proto_init() } +func file_IssuingAttributes_proto_init() { + if File_IssuingAttributes_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_IssuingAttributes_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IssuingAttributes); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_IssuingAttributes_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Definition); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_IssuingAttributes_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_IssuingAttributes_proto_goTypes, + DependencyIndexes: file_IssuingAttributes_proto_depIdxs, + MessageInfos: file_IssuingAttributes_proto_msgTypes, + }.Build() + File_IssuingAttributes_proto = out.File + file_IssuingAttributes_proto_rawDesc = nil + file_IssuingAttributes_proto_goTypes = nil + file_IssuingAttributes_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ThirdPartyAttribute.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ThirdPartyAttribute.pb.go new file mode 100644 index 0000000..11623d2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk/v3@v3.14.0/yotiprotoshare/ThirdPartyAttribute.pb.go @@ -0,0 +1,177 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.18.1 +// source: ThirdPartyAttribute.proto + +package yotiprotoshare + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ThirdPartyAttribute struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IssuanceToken []byte `protobuf:"bytes,1,opt,name=issuance_token,json=issuanceToken,proto3" json:"issuance_token,omitempty"` + IssuingAttributes *IssuingAttributes `protobuf:"bytes,2,opt,name=issuing_attributes,json=issuingAttributes,proto3" json:"issuing_attributes,omitempty"` +} + +func (x *ThirdPartyAttribute) Reset() { + *x = ThirdPartyAttribute{} + if protoimpl.UnsafeEnabled { + mi := &file_ThirdPartyAttribute_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThirdPartyAttribute) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThirdPartyAttribute) ProtoMessage() {} + +func (x *ThirdPartyAttribute) ProtoReflect() protoreflect.Message { + mi := &file_ThirdPartyAttribute_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThirdPartyAttribute.ProtoReflect.Descriptor instead. +func (*ThirdPartyAttribute) Descriptor() ([]byte, []int) { + return file_ThirdPartyAttribute_proto_rawDescGZIP(), []int{0} +} + +func (x *ThirdPartyAttribute) GetIssuanceToken() []byte { + if x != nil { + return x.IssuanceToken + } + return nil +} + +func (x *ThirdPartyAttribute) GetIssuingAttributes() *IssuingAttributes { + if x != nil { + return x.IssuingAttributes + } + return nil +} + +var File_ThirdPartyAttribute_proto protoreflect.FileDescriptor + +var file_ThirdPartyAttribute_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x41, 0x74, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, 0x1a, 0x17, 0x49, 0x73, 0x73, + 0x75, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, + 0x72, 0x74, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x69, 0x73, 0x73, 0x75, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x69, 0x73, 0x73, 0x75, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x50, 0x0a, 0x12, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x5f, 0x61, + 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x73, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x31, + 0x2e, 0x49, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x65, 0x73, 0x52, 0x11, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x42, 0xef, 0x01, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x79, 0x6f, + 0x74, 0x69, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x73, 0x70, + 0x69, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x18, + 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, + 0x75, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x65, 0x74, 0x79, 0x6f, 0x74, 0x69, 0x2f, 0x79, 0x6f, 0x74, + 0x69, 0x2d, 0x67, 0x6f, 0x2d, 0x73, 0x64, 0x6b, 0x2f, 0x76, 0x33, 0x2f, 0x79, 0x6f, 0x74, 0x69, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x61, 0x72, 0x65, 0xaa, 0x02, 0x18, 0x59, 0x6f, 0x74, + 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x75, 0x66, 0x2e, + 0x53, 0x68, 0x61, 0x72, 0x65, 0xca, 0x02, 0x19, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, + 0x69, 0xe2, 0x02, 0x25, 0x59, 0x6f, 0x74, 0x69, 0x5c, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x5c, 0x53, 0x68, 0x61, 0x72, 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x5c, 0x47, 0x50, + 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1b, 0x59, 0x6f, 0x74, 0x69, + 0x3a, 0x3a, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x3a, 0x3a, 0x53, 0x68, 0x61, 0x72, + 0x65, 0x70, 0x75, 0x62, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_ThirdPartyAttribute_proto_rawDescOnce sync.Once + file_ThirdPartyAttribute_proto_rawDescData = file_ThirdPartyAttribute_proto_rawDesc +) + +func file_ThirdPartyAttribute_proto_rawDescGZIP() []byte { + file_ThirdPartyAttribute_proto_rawDescOnce.Do(func() { + file_ThirdPartyAttribute_proto_rawDescData = protoimpl.X.CompressGZIP(file_ThirdPartyAttribute_proto_rawDescData) + }) + return file_ThirdPartyAttribute_proto_rawDescData +} + +var file_ThirdPartyAttribute_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_ThirdPartyAttribute_proto_goTypes = []interface{}{ + (*ThirdPartyAttribute)(nil), // 0: sharepubapi_v1.ThirdPartyAttribute + (*IssuingAttributes)(nil), // 1: sharepubapi_v1.IssuingAttributes +} +var file_ThirdPartyAttribute_proto_depIdxs = []int32{ + 1, // 0: sharepubapi_v1.ThirdPartyAttribute.issuing_attributes:type_name -> sharepubapi_v1.IssuingAttributes + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_ThirdPartyAttribute_proto_init() } +func file_ThirdPartyAttribute_proto_init() { + if File_ThirdPartyAttribute_proto != nil { + return + } + file_IssuingAttributes_proto_init() + if !protoimpl.UnsafeEnabled { + file_ThirdPartyAttribute_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThirdPartyAttribute); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_ThirdPartyAttribute_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_ThirdPartyAttribute_proto_goTypes, + DependencyIndexes: file_ThirdPartyAttribute_proto_depIdxs, + MessageInfos: file_ThirdPartyAttribute_proto_msgTypes, + }.Build() + File_ThirdPartyAttribute_proto = out.File + file_ThirdPartyAttribute_proto_rawDesc = nil + file_ThirdPartyAttribute_proto_goTypes = nil + file_ThirdPartyAttribute_proto_depIdxs = nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/.gitignore b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/.gitignore new file mode 100644 index 0000000..b79bd93 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/.gitignore @@ -0,0 +1,12 @@ +examples/profile/images/YotiSelfie.jpeg +.vscode + +# Test binary, build with `go test -c` +*.test + +# 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/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/CONTRIBUTING.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/CONTRIBUTING.md new file mode 100644 index 0000000..3ba94fb --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +The command `go get "github.com/getyoti/yoti-go-sdk"` downloads the Yoti package, along with its dependencies, and installs it. + +## Commit Process + +1) `goimports` formats the code and sanitises imports +1) `go vet` reports suspicious constructs +1) `go test` to run the tests + +## VS Code + +For developing in VS Code, use the following `launch.json` file (placed inside a `.vscode` folder) to easily run the examples from VS Code: + +```javascript +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "AML Example", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}/examples/aml/main.go" + }, + { + "name": "Example", + "type": "go", + "request": "launch", + "mode": "debug", + "remotePath": "", + "host": "127.0.0.1", + "program": "${workspaceFolder}/examples/profile/main.go", + "env": {}, + "args": ["certificatehelper.go"], + "showLog": true + } + ] +} +``` \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/LICENSE.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/LICENSE.md new file mode 100644 index 0000000..fefdd34 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/LICENSE.md @@ -0,0 +1,23 @@ +# MIT License + +Copyright © 2017 Yoti Ltd + +* * * + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/README.md b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/README.md new file mode 100644 index 0000000..f5d3580 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/README.md @@ -0,0 +1,339 @@ +# Yoti Go SDK + +Welcome to the Yoti Go SDK. This repo contains the tools and step by step instructions you need to quickly integrate your Go back-end with Yoti so that your users can share their identity details with your application in a secure and trusted way. + +## Table of Contents + +1) [An Architectural view](#an-architectural-view) - +High level overview of integration + +1) [Installing the SDK](#installing-the-sdk) - +How to install our SDK + +1) [SDK Project import](#sdk-project-import) - +How to install the SDK to your project + +1) [Configuration](#configuration) - +How to initialise your configuration + +1) [Profile Retrieval](#profile-retrieval) - +How to retrieve a Yoti profile using the one time use token + +1) [Handling users](#handling-users) - +How to manage users + +1) [AML Integration](#aml-integration) - +How to integrate with Yoti's AML (Anti Money Laundering) service + +1) [Running the tests](#running-the-tests) - +Attributes defined + +1) [Running the example](#running-the-profile-example) - +Attributes defined + +1) [API Coverage](#api-coverage) - +Attributes defined + +1) [Support](#support) - +Please feel free to reach out + +1) [References](#references) + +## An Architectural View + +Before you start your integration, here is a bit of background on how the integration works. To integrate your application with Yoti, your back-end must expose a GET endpoint that Yoti will use to forward tokens. +The endpoint is configured in the [Yoti Dashboard](https://www.yoti.com/dashboard) where you create/update your application. For more information on how to create an application please check our [developer page](https://www.yoti.com/developers/documentation/#login-button-setup). + +The image below shows how your application back-end and Yoti integrate into the context of a Login flow. +Yoti SDK carries out for you steps 6, 7 ,8 and the profile decryption in step 9. + +![alt text](login_flow.png "Login flow") + +Yoti also allows you to enable user details verification from your mobile app by means of the Android (TBA) and iOS (TBA) SDKs. In that scenario, your Yoti-enabled mobile app is playing both the role of the browser and the Yoti app. Your back-end doesn't need to handle these cases in a significantly different way. You might just decide to handle the `User-Agent` header in order to provide different responses for desktop and mobile clients. + +## Installing the SDK + +To download and install the Yoti SDK and its dependencies, simply run the following command from your terminal: + +```Go +go get "github.com/getyoti/yoti-go-sdk" +``` + +## SDK Project import + +You can reference the project URL by adding the following import: + +```Go +import "github.com/getyoti/yoti-go-sdk" +``` + +## Configuration + +The YotiClient is the SDK entry point. To initialise it you need include the following snippet inside your endpoint initialisation section: + +```Go +sdkID := "your-sdk-id"; +key, err := ioutil.ReadFile("path/to/your-application-pem-file.pem") +if err != nil { + // handle key load error +} + +client := yoti.Client{ + SdkID: sdkID, + Key: key} +``` + +Where: + +* `sdkID` is the SDK identifier generated by Yoti Dashboard in the Key tab when you create your app. Note this is not your Application Identifier which is needed by your client-side code. + +* `path/to/your-application-pem-file.pem` is the path to the application pem file. It can be downloaded from the Keys tab in the [Yoti Dashboard](https://www.yoti.com/dashboard/applications). + +Please do not open the pem file as this might corrupt the key and you will need to create a new application. + +Keeping your settings and access keys outside your repository is highly recommended. You can use gems like [godotenv](https://github.com/joho/godotenv) to manage environment variables more easily. + +## Profile Retrieval + +When your application receives a one time use token via the exposed endpoint (it will be assigned to a query string parameter named `token`), you can easily retrieve the activity details by adding the following to your endpoint handler: + +```Go +activityDetails, errStrings := client.GetActivityDetails(yotiOneTimeUseToken) +if len(errStrings) != 0 { + // handle unhappy path +} +``` + +### Profile + +You can then get the user profile from the activityDetails struct: + +```Go +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 +} else { + dateOfBirth = dobAttr.Value() +} +var structuredPostalAddress map[string]interface{} +structuredPostalAddressAttribute, err := userProfile.StructuredPostalAddress() +if err != nil { + //handle error +} else { + structuredPostalAddress := structuredPostalAddressAttribute.Value().(map[string]interface{}) +} +``` + +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) +``` + +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 you 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() +``` + +You can also retrieve further properties from these respective anchors in the following way: + +```Go +var givenNamesFirstAnchor *anchor.Anchor = givenNamesAnchors[0] + +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 + +When you retrieve the user profile, you receive a user ID generated by Yoti exclusively for your application. +This means that if the same individual logs into another app, Yoti will assign her/him a different ID. +You can use this ID to verify whether (for your application) the retrieved profile identifies a new or an existing user. +Here is an example of how this works: + +```Go +activityDetails, err := client.GetActivityDetails(yotiOneTimeUseToken) +if err == nil { + user := YourUserSearchFunction(activityDetails.RememberMeID()) + if user != nil { + // handle login + } else { + // handle registration + } +} else { + // handle unhappy path +} +``` + +Where `yourUserSearchFunction` is a piece of logic in your app that is supposed to find a user, given a RememberMeID. +No matter if the user is a new or an existing one, Yoti will always provide her/his profile, so you don't necessarily need to store it. + +The `profile` object provides a set of attributes corresponding to user attributes. Whether the attributes are present or not depends on the settings you have applied to your app on Yoti Dashboard. + +## Running the Tests + +You can run the unit tests for this project by executing the following commands inside the repository folder + +```Go +go get -t +go test +``` + +## AML Integration + +Yoti provides an AML (Anti Money Laundering) check service to allow a deeper KYC process to prevent fraud. This is a chargeable service, so please contact [sdksupport@yoti.com](mailto:sdksupport@yoti.com) for more information. + +Yoti will provide a boolean result on the following checks: + +* PEP list - Verify against Politically Exposed Persons list +* Fraud list - Verify against US Social Security Administration Fraud (SSN Fraud) list +* Watch list - Verify against watch lists from the Office of Foreign Assets Control + +To use this functionality you must ensure your application is assigned to your Organisation in the Yoti Dashboard - please see [here](https://www.yoti.com/developers/documentation/#1-creating-an-organisation) for further information. + +For the AML check you will need to provide the following: + +* Data provided by Yoti (please ensure you have selected the Given name(s) and Family name attributes from the Data tab in the Yoti Dashboard) + * Given name(s) + * Family name +* Data that must be collected from the user: + * Country of residence (must be an ISO 3166 3-letter code) + * Social Security Number (US citizens only) + * Postcode/Zip code (US citizens only) + +### Consent + +Performing an AML check on a person *requires* their consent. +**You must ensure you have user consent *before* using this service.** + +### Code Example + +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 +givenNames := "Edward Richard George" +familyName := "Heath" + +amlAddress := yoti.AmlAddress{ + Country: "GBR"} + +amlProfile := yoti.AmlProfile{ + GivenNames: givenNames, + FamilyName: familyName, + Address: amlAddress} + +result, err := client.PerformAmlCheck(amlProfile) + +log.Printf( + "AML Result for %s %s:", + givenNames, + familyName) +log.Printf( + "On PEP list: %s", + strconv.FormatBool(result.OnPEPList)) +log.Printf( + "On Fraud list: %s", + strconv.FormatBool(result.OnFraudList)) +log.Printf( + "On Watch list: %s", + strconv.FormatBool(result.OnWatchList)) +} +``` + +Additionally, an [example AML application](/examples/aml/main.go) is provided in the examples folder. + +* Rename the [.env.example](examples/profile/.env.example) file to `.env` and fill in the required configuration values (mentioned in the [Configuration](#configuration) section) +* Change directory to the aml example folder: `cd examples/aml` +* Install the dependencies with `go get` +* Start the example with `go run main.go` + +## Running the Profile Example + +The profile retrieval example can be found in the [examples folder](examples). + +* Change directory to the profile example folder: `cd examples/profile` +* On the [Yoti Dashboard](https://www.yoti.com/dashboard/applications): + * Set the application domain of your app to `localhost:8080` + * Set the scenario callback URL to `/profile` +* Rename the [.env.example](examples/profile/.env.example) file to `.env` and fill in the required configuration values (mentioned in the [Configuration](#configuration) section) +* Install the dependencies with `go get` +* Start the server with `go run main.go certificatehelper.go` + +Visiting `https://localhost:8080/` should show a Yoti Connect button + +## API Coverage + +* [X] Activity Details + * [X] Remember Me ID `RememberMeID()` + * [X] User Profile `UserProfile` + * [X] Selfie `Selfie()` + * [X] Selfie Base64 URL `Selfie().Value().Base64URL()` + * [X] Given Names `GivenNames()` + * [X] Family Name `FamilyName()` + * [X] Full Name `FullName()` + * [X] Mobile Number `MobileNumber()` + * [X] Email Address `EmailAddress()` + * [X] Date of Birth `DateOfBirth()` + * [X] Postal Address `Address()` + * [X] Structured Postal Address `StructuredPostalAddress()` + * [X] Gender `Gender()` + * [X] Nationality `Nationality()` + +## Support + +For any questions or support please email [sdksupport@yoti.com](mailto:sdksupport@yoti.com). +Please provide the following to get you up and working as quickly as possible: + +* Computer type +* OS version +* Version of Go being used +* Screenshot + +Once we have answered your question we may contact you again to discuss Yoti products and services. If you’d prefer us not to do this, please let us know when you e-mail. + +## References + +* [AES-256 symmetric encryption][] +* [RSA pkcs asymmetric encryption][] +* [Protocol buffers][] +* [Base64 data][] + +[AES-256 symmetric encryption]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard +[RSA pkcs asymmetric encryption]: https://en.wikipedia.org/wiki/RSA_(cryptosystem) +[Protocol buffers]: https://en.wikipedia.org/wiki/Protocol_Buffers +[Base64 data]: https://en.wikipedia.org/wiki/Base64 diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activitydetails.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activitydetails.go new file mode 100644 index 0000000..1758b6f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activitydetails.go @@ -0,0 +1,14 @@ +package yoti + +// ActivityDetails represents the result of an activity between a user and the application. +type ActivityDetails struct { + UserProfile Profile + rememberMeID string +} + +// RememberMeID is a unique identifier Yoti assigns to your user, but only for your app. +// If the same user logs into your app again, you get the same id. +// If she/he logs into another application, Yoti will assign a different id for that app. +func (a ActivityDetails) RememberMeID() string { + return a.rememberMeID +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activityerrors.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activityerrors.go new file mode 100644 index 0000000..b26a1b7 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/activityerrors.go @@ -0,0 +1,12 @@ +package yoti + +import "errors" + +var ( + // ErrProfileNotFound profile was not found during activity retrieval for the provided one time use token + ErrProfileNotFound = errors.New("ProfileNotFound") + // ErrFailure there was a failure during activity retrieval + ErrFailure = errors.New("Failure") + // ErrSharingFailure there was a failure when sharing + ErrSharingFailure = errors.New("SharingFailure") +) diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/aml.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/aml.go new file mode 100644 index 0000000..544280e --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/aml.go @@ -0,0 +1,49 @@ +package yoti + +import ( + "encoding/json" + "log" +) + +// AmlAddress Address for Anti Money Laundering (AML) purposes +type AmlAddress struct { + Country string `json:"country"` + Postcode string `json:"post_code"` +} + +// AmlProfile User profile for Anti Money Laundering (AML) checks +type AmlProfile struct { + GivenNames string `json:"given_names"` + FamilyName string `json:"family_name"` + Address AmlAddress `json:"address"` + SSN string `json:"ssn"` +} + +// AmlResult Result of Anti Money Laundering (AML) check for a particular user +type AmlResult struct { + OnFraudList bool `json:"on_fraud_list"` + OnPEPList bool `json:"on_pep_list"` + OnWatchList bool `json:"on_watch_list"` +} + +// Deprecated: Will be removed in v3.0.0, please use GetAmlResult below instead. Parses AML result from response +func GetAmlResultFromResponse(amlResponse []byte) AmlResult { + var amlResult AmlResult + json.Unmarshal(amlResponse, &amlResult) + + return amlResult +} + +// GetAmlResult Parses AML result from response +func GetAmlResult(amlResponse []byte) (AmlResult, error) { + var amlResult AmlResult + err := json.Unmarshal(amlResponse, &amlResult) + + if err != nil { + log.Printf( + "Unable to get AML result from response. Error: %s", err) + return amlResult, err + } + + return amlResult, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchorparser.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchorparser.go new file mode 100644 index 0000000..efb5e16 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchorparser.go @@ -0,0 +1,114 @@ +package anchor + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + "log" + + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" + "github.com/getyoti/yoti-go-sdk/yotiprotocom" + "github.com/golang/protobuf/proto" +) + +type anchorExtension struct { + Extension string `asn1:"tag:0,utf8"` +} + +var ( + sourceOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 47127, 1, 1, 1} + verifierOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 47127, 1, 1, 2} +) + +// ParseAnchors takes a slice of protobuf anchors, parses them, and returns a slice of Yoti SDK Anchors +func ParseAnchors(protoAnchors []*yotiprotoattr.Anchor) []*Anchor { + var processedAnchors []*Anchor + for _, protoAnchor := range protoAnchors { + var extensions []string + var ( + anchorType = AnchorTypeUnknown + parsedCerts = parseCertificates(protoAnchor.OriginServerCerts) + ) + for _, cert := range parsedCerts { + for _, ext := range cert.Extensions { + var ( + value string + err error + ) + anchorType, value, err = parseExtension(ext) + if err != nil { + log.Printf("error parsing anchor extension, %v", err) + continue + } else if anchorType == AnchorTypeUnknown { + continue + } + extensions = append(extensions, value) + } + } + + processedAnchor := newAnchor(anchorType, parsedCerts, parseSignedTimestamp(protoAnchor.SignedTimeStamp), protoAnchor.SubType, extensions) + + processedAnchors = append(processedAnchors, processedAnchor) + } + + return processedAnchors +} + +func parseExtension(ext pkix.Extension) (anchorType Type, val string, err error) { + anchorType = AnchorTypeUnknown + + switch { + case ext.Id.Equal(sourceOID): + anchorType = AnchorTypeSource + case ext.Id.Equal(verifierOID): + anchorType = AnchorTypeVerifier + default: + return anchorType, "", nil + } + + var ae anchorExtension + _, err = asn1.Unmarshal(ext.Value, &ae) + switch { + case err != nil: + return anchorType, "", fmt.Errorf("unable to unmarshal extension: %v", err) + case len(ae.Extension) == 0: + return anchorType, "", errors.New("empty extension") + default: + val = ae.Extension + } + + return anchorType, val, nil +} + +func unmarshalExtension(extensionValue []byte) string { + var ae anchorExtension + + _, err := asn1.Unmarshal(extensionValue, &ae) + if err == nil && ae.Extension != "" { + return ae.Extension + } + + log.Printf("Error unmarshalling anchor extension: %q", err) + return "" +} + +func parseSignedTimestamp(rawBytes []byte) yotiprotocom.SignedTimestamp { + signedTimestamp := &yotiprotocom.SignedTimestamp{} + if err := proto.Unmarshal(rawBytes, signedTimestamp); err != nil { + signedTimestamp = nil + } + + return *signedTimestamp +} + +func parseCertificates(rawCerts [][]byte) (result []*x509.Certificate) { + for _, cert := range rawCerts { + parsedCertificate, _ := x509.ParseCertificate(cert) + + result = append(result, parsedCertificate) + } + + return result +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchors.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchors.go new file mode 100644 index 0000000..276b63f --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/anchors.go @@ -0,0 +1,105 @@ +package anchor + +import ( + "crypto/x509" + + "github.com/getyoti/yoti-go-sdk/yotiprotocom" +) + +// Anchor is the metadata associated with an attribute. It describes how an attribute has been provided +// to Yoti (SOURCE Anchor) and how it has been verified (VERIFIER Anchor). +// If an attribute has only one SOURCE Anchor with the value set to +// "USER_PROVIDED" and zero VERIFIER Anchors, then the attribute +// is a self-certified one. +type Anchor struct { + anchorType Type + originServerCerts []*x509.Certificate + signedTimestamp SignedTimestamp + subtype string + value []string +} + +func newAnchor(anchorType Type, originServerCerts []*x509.Certificate, signedTimestamp yotiprotocom.SignedTimestamp, subtype string, value []string) *Anchor { + return &Anchor{ + anchorType: anchorType, + originServerCerts: originServerCerts, + signedTimestamp: convertSignedTimestamp(signedTimestamp), + subtype: subtype, + value: value, + } +} + +// Type Anchor type, based on the Object Identifier (OID) +type Type int + +const ( + // AnchorTypeUnknown - default value + AnchorTypeUnknown Type = 1 + iota + // AnchorTypeSource - how the anchor has been sourced + AnchorTypeSource + // AnchorTypeVerifier - how the anchor has been verified + AnchorTypeVerifier +) + +// Type of the Anchor - most likely either SOURCE or VERIFIER, but it's +// possible that new Anchor types will be added in future. +func (a Anchor) Type() Type { + return a.anchorType +} + +// OriginServerCerts are the X.509 certificate chain(DER-encoded ASN.1) +// from the service that assigned the attribute. +// +// The first certificate in the chain holds the public key that can be +// used to verify the Signature field; any following entries (zero or +// more) are for intermediate certificate authorities (in order). +// +// The last certificate in the chain must be verified against the Yoti root +// CA certificate. An extension in the first certificate holds the main artifact type, +// e.g. “PASSPORT”, which can be retrieved with .Value(). +func (a Anchor) OriginServerCerts() []*x509.Certificate { + return a.originServerCerts +} + +// SignedTimestamp is the time at which the signature was created. The +// 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 *time.Time can be called with +// .Timestamp() on the result of this function. +func (a Anchor) SignedTimestamp() SignedTimestamp { + return a.signedTimestamp +} + +// SubType is an indicator of any specific processing method, or +// subcategory, pertaining to an artifact. For example, for a passport, this would be +// either "NFC" or "OCR". +func (a Anchor) SubType() string { + return a.subtype +} + +// Value identifies the provider that either sourced or verified the attribute value. +// The range of possible values is not limited. For a SOURCE anchor, expect values like +// PASSPORT, DRIVING_LICENSE. For a VERIFIER anchor expect valuues like YOTI_ADMIN. +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.anchorType == anchorType { + result = append(result, v) + } + } + return result +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/signedtimestamp.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/signedtimestamp.go new file mode 100644 index 0000000..76f5529 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/anchor/signedtimestamp.go @@ -0,0 +1,35 @@ +package anchor + +import ( + "time" + + "github.com/getyoti/yoti-go-sdk/yotiprotocom" +) + +// SignedTimestamp is the object which contains a timestamp +type SignedTimestamp struct { + version int32 + timestamp *time.Time +} + +func convertSignedTimestamp(protoSignedTimestamp yotiprotocom.SignedTimestamp) SignedTimestamp { + uintTimestamp := protoSignedTimestamp.Timestamp + intTimestamp := int64(uintTimestamp) + unixTime := time.Unix(intTimestamp/1000000, 0) + + return SignedTimestamp{ + version: protoSignedTimestamp.Version, + timestamp: &unixTime, + } +} + +// Version indicates both the version of the protobuf message in use, +// as well as the specific hash algorithms. +func (s SignedTimestamp) Version() int32 { + return s.version +} + +// Timestamp is a point in time, to the nearest microsecond. +func (s SignedTimestamp) Timestamp() *time.Time { + return s.timestamp +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/genericattribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/genericattribute.go new file mode 100644 index 0000000..f0bc1ed --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/genericattribute.go @@ -0,0 +1,84 @@ +package attribute + +import ( + "log" + "time" + + "github.com/getyoti/yoti-go-sdk/anchor" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" +) + +// GenericAttribute is a Yoti attribute which returns a generic value +type GenericAttribute struct { + *yotiprotoattr.Attribute + value interface{} + anchors []*anchor.Anchor +} + +// NewGeneric creates a new generic attribute +func NewGeneric(a *yotiprotoattr.Attribute) *GenericAttribute { + var value interface{} + + switch a.ContentType { + case yotiprotoattr.ContentType_DATE: + parsedTime, err := time.Parse("2006-01-02", string(a.Value)) + if err == nil { + value = &parsedTime + } else { + log.Printf("Unable to parse date value: %q. Error: %q", string(a.Value), err) + } + + case yotiprotoattr.ContentType_JSON: + unmarshalledJSON, err := UnmarshallJSON(a.Value) + + if err == nil { + value = unmarshalledJSON + } else { + log.Printf("Unable to parse JSON value: %q. Error: %q", string(a.Value), err) + } + + case yotiprotoattr.ContentType_STRING: + value = string(a.Value) + + case yotiprotoattr.ContentType_JPEG, + yotiprotoattr.ContentType_PNG, + yotiprotoattr.ContentType_UNDEFINED: + value = a.Value + + default: + value = a.Value + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &GenericAttribute{ + Attribute: &yotiprotoattr.Attribute{ + Name: a.Name, + ContentType: a.ContentType, + }, + value: value, + anchors: 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). +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 anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *GenericAttribute) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/image.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/image.go new file mode 100644 index 0000000..80fc242 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/image.go @@ -0,0 +1,33 @@ +package attribute + +import ( + "encoding/base64" + "fmt" +) + +const ( + // ImageTypeJpeg JPEG format + ImageTypeJpeg string = "jpeg" + // ImageTypePng PNG format + ImageTypePng string = "png" +) + +// Image format of the image and the image data +type Image struct { + Type string + Data []byte +} + +// 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 string) string { + return fmt.Sprintf("image/%v", imageType) +} + +// Base64URL is the Image encoded as a base64 URL +func (image *Image) Base64URL() string { + base64EncodedImage := base64.StdEncoding.EncodeToString(image.Data) + contentType := GetMIMEType(image.Type) + + return "data:" + contentType + ";base64;," + base64EncodedImage +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/imageattribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/imageattribute.go new file mode 100644 index 0000000..9ddafbe --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/imageattribute.go @@ -0,0 +1,67 @@ +package attribute + +import ( + "errors" + + "github.com/getyoti/yoti-go-sdk/anchor" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" +) + +// ImageAttribute is a Yoti attribute which returns an image as its value +type ImageAttribute struct { + *yotiprotoattr.Attribute + value *Image + anchors []*anchor.Anchor +} + +// NewImage creates a new Image attribute +func NewImage(a *yotiprotoattr.Attribute) (*ImageAttribute, error) { + var imageType string + + switch a.ContentType { + case yotiprotoattr.ContentType_JPEG: + imageType = ImageTypeJpeg + + case yotiprotoattr.ContentType_PNG: + imageType = ImageTypePng + + default: + return nil, errors.New("Cannot create ImageAttribute with unsupported type") + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &ImageAttribute{ + Attribute: &yotiprotoattr.Attribute{ + Name: a.Name, + ContentType: a.ContentType, + }, + value: &Image{ + Data: a.Value, + Type: imageType, + }, + anchors: parsedAnchors, + }, 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). +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 anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *ImageAttribute) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/jsonattribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/jsonattribute.go new file mode 100644 index 0000000..e71ab69 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/jsonattribute.go @@ -0,0 +1,70 @@ +package attribute + +import ( + "encoding/json" + "fmt" + + "github.com/getyoti/yoti-go-sdk/anchor" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" +) + +// JSONAttribute is a Yoti attribute which returns an interface as its value +type JSONAttribute struct { + *yotiprotoattr.Attribute // Value returns the value of a JSON attribute in the form of an interface + value interface{} + anchors []*anchor.Anchor +} + +// NewJSON creates a new JSON attribute +func NewJSON(a *yotiprotoattr.Attribute) (*JSONAttribute, error) { + interfaceValue, err := UnmarshallJSON(a.Value) + if err != nil { + err = fmt.Errorf("Unable to parse JSON value: %q. Error: %q", a.Value, err) + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &JSONAttribute{ + Attribute: &yotiprotoattr.Attribute{ + Name: a.Name, + ContentType: a.ContentType, + }, + value: interfaceValue, + anchors: parsedAnchors, + }, nil +} + +// UnmarshallJSON unmarshalls JSON into an interface +func UnmarshallJSON(byteValue []byte) (result interface{}, err error) { + var unmarshalledJSON interface{} + err = json.Unmarshal(byteValue, &unmarshalledJSON) + + if err != nil { + return nil, err + } + + 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). +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 anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *JSONAttribute) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/stringattribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/stringattribute.go new file mode 100644 index 0000000..018c8af --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/stringattribute.go @@ -0,0 +1,49 @@ +package attribute + +import ( + "github.com/getyoti/yoti-go-sdk/anchor" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" +) + +// StringAttribute is a Yoti attribute which returns a string as its value +type StringAttribute struct { + *yotiprotoattr.Attribute + value string + anchors []*anchor.Anchor +} + +// NewString creates a new String attribute +func NewString(a *yotiprotoattr.Attribute) *StringAttribute { + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &StringAttribute{ + Attribute: &yotiprotoattr.Attribute{ + Name: a.Name, + ContentType: a.ContentType, + }, + value: string(a.Value), + anchors: 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). +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 anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *StringAttribute) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/timeattribute.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/timeattribute.go new file mode 100644 index 0000000..450a055 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/attribute/timeattribute.go @@ -0,0 +1,59 @@ +package attribute + +import ( + "log" + "time" + + "github.com/getyoti/yoti-go-sdk/anchor" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" +) + +// TimeAttribute is a Yoti attribute which returns a time as its value +type TimeAttribute struct { + *yotiprotoattr.Attribute + value *time.Time + anchors []*anchor.Anchor +} + +// NewTime creates a new Time attribute +func NewTime(a *yotiprotoattr.Attribute) (*TimeAttribute, error) { + parsedTime, err := time.Parse("2006-01-02", string(a.Value)) + if err != nil { + log.Printf("Unable to parse time value of: %q. Error: %q", a.Value, err) + parsedTime = time.Time{} + return nil, err + } + + parsedAnchors := anchor.ParseAnchors(a.Anchors) + + return &TimeAttribute{ + Attribute: &yotiprotoattr.Attribute{ + Name: a.Name, + ContentType: a.ContentType, + }, + value: &parsedTime, + anchors: 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). +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 anchor.GetSources(a.anchors) +} + +// Verifiers returns the anchors which identify how and when an attribute value was verified by another provider. +func (a *TimeAttribute) Verifiers() []*anchor.Anchor { + return anchor.GetVerifiers(a.anchors) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/conversion.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/conversion.go new file mode 100644 index 0000000..34739fe --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/conversion.go @@ -0,0 +1,28 @@ +package yoti + +import ( + "encoding/base64" +) + +func bytesToUtf8(bytes []byte) string { + return string(bytes) +} + +func bytesToBase64(bytes []byte) string { + return base64.StdEncoding.EncodeToString(bytes) +} + +func utfToBytes(utf8 string) []byte { + return []byte(utf8) +} + +func base64ToBytes(base64Str string) ([]byte, error) { + return base64.StdEncoding.DecodeString(base64Str) +} + +/* UrlSafe Base64 uses '-' and '_' instead of '+' and '/' respectively so it can be passed + * as a url parameter without extra encoding. + */ +func urlSafeBase64ToBytes(urlSafeBase64 string) ([]byte, error) { + return base64.URLEncoding.DecodeString(urlSafeBase64) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/crypto.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/crypto.go new file mode 100644 index 0000000..5689888 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/crypto.go @@ -0,0 +1,149 @@ +package yoti + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" +) + +func loadRsaKey(keyBytes []byte) (*rsa.PrivateKey, error) { + // Extract the PEM-encoded data + block, _ := pem.Decode(keyBytes) + + if block == nil { + return nil, errors.New("not PEM-encoded") + } + + if block.Type != "RSA PRIVATE KEY" { + return nil, errors.New("not RSA private key") + } + + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, errors.New("bad RSA private key") + } + + return key, nil +} + +func decryptRsa(cipherBytes []byte, key *rsa.PrivateKey) ([]byte, error) { + return rsa.DecryptPKCS1v15(rand.Reader, key, cipherBytes) +} + +func decipherAes(key, iv, cipherBytes []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return []byte{}, err + } + + // CBC mode always works in whole blocks. + if (len(cipherBytes) % aes.BlockSize) != 0 { + return []byte{}, errors.New("ciphertext is not a multiple of the block size") + } + + mode := cipher.NewCBCDecrypter(block, iv) + + decipheredBytes := make([]byte, len(cipherBytes)) + + mode.CryptBlocks(decipheredBytes, cipherBytes) + + return pkcs7Unpad(decipheredBytes, aes.BlockSize) +} + +func pkcs7Unpad(ciphertext []byte, blocksize int) (result []byte, err error) { + if blocksize <= 0 { + err = fmt.Errorf("blocksize %d is not valid for padding removal", blocksize) + return + } + if len(ciphertext) == 0 { + err = errors.New("Cannot remove padding on empty byte array") + return + } + if len(ciphertext)%blocksize != 0 { + err = errors.New("ciphertext is not a multiple of the block size") + return + } + + c := ciphertext[len(ciphertext)-1] + n := int(c) + if n == 0 || n > len(ciphertext) { + err = errors.New("ciphertext is not padded with PKCS#7 padding") + return + } + + // verify all padding bytes are correct + for i := 0; i < n; i++ { + if ciphertext[len(ciphertext)-n+i] != c { + err = errors.New("ciphertext is not padded with PKCS#7 padding") + return + } + } + return ciphertext[:len(ciphertext)-n], nil +} + +func signDigest(digest []byte, key *rsa.PrivateKey) ([]byte, error) { + hashed := sha256.Sum256(digest) + + signedDigest, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hashed[:]) + if err != nil { + return []byte{}, err + } + + return signedDigest, nil +} + +func getAuthKey(key *rsa.PrivateKey) (string, error) { + return getDerEncodedPublicKey(key) +} + +func getDerEncodedPublicKey(key *rsa.PrivateKey) (result string, err error) { + var derEncodedBytes []byte + if derEncodedBytes, err = x509.MarshalPKIXPublicKey(key.Public()); err != nil { + return + } + + result = bytesToBase64(derEncodedBytes) + return +} + +func generateNonce() (string, error) { + b := make([]byte, 16) + _, err := rand.Read(b) + if err != nil { + return "", err + } + + uuid := fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + + return uuid, nil +} + +func decryptToken(encryptedConnectToken string, key *rsa.PrivateKey) (result string, err error) { + // token was encoded as a urlsafe base64 so it can be transfered in a url + var cipherBytes []byte + if cipherBytes, err = urlSafeBase64ToBytes(encryptedConnectToken); err != nil { + return "", err + } + + var decipheredBytes []byte + if decipheredBytes, err = decryptRsa(cipherBytes, key); err != nil { + return "", err + } + + return bytesToUtf8(decipheredBytes), nil +} + +func unwrapKey(wrappedKey string, key *rsa.PrivateKey) (result []byte, err error) { + var cipherBytes []byte + if cipherBytes, err = base64ToBytes(wrappedKey); err != nil { + return nil, err + } + return decryptRsa(cipherBytes, key) +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/dataobjects.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/dataobjects.go new file mode 100644 index 0000000..f2b36be --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/dataobjects.go @@ -0,0 +1,20 @@ +package yoti + +type receiptDO struct { + ReceiptID string `json:"receipt_id"` + OtherPartyProfileContent string `json:"other_party_profile_content"` + ProfileContent string `json:"profile_content"` + OtherPartyExtraDataContent string `json:"other_party_extra_data_content"` + ExtraDataContent string `json:"extra_data_content"` + WrappedReceiptKey string `json:"wrapped_receipt_key"` + PolicyURI string `json:"policy_uri"` + PersonalKey string `json:"personal_key"` + RememberMeID string `json:"remember_me_id"` + SharingOutcome string `json:"sharing_outcome"` + Timestamp string `json:"timestamp"` +} + +type profileDO struct { + SessionData string `json:"session_data"` + Receipt receiptDO `json:"receipt"` +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.env.example b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.env.example new file mode 100644 index 0000000..36f05ae --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.env.example @@ -0,0 +1,2 @@ +YOTI_CLIENT_SDK_ID= +YOTI_KEY_FILE_PATH= diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.gitignore b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/.gitignore @@ -0,0 +1 @@ +.env diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/main.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/main.go new file mode 100644 index 0000000..6a7e30a --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/aml/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "io/ioutil" + "log" + "os" + "strconv" + + yoti "github.com/getyoti/yoti-go-sdk" + _ "github.com/joho/godotenv/autoload" +) + +var ( + sdkID string + key []byte + client *yoti.Client +) + +func main() { + 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 + } + + client = &yoti.Client{ + SdkID: sdkID, + Key: key} + + givenNames := "Edward Richard George" + familyName := "Heath" + + amlAddress := yoti.AmlAddress{ + Country: "GBR"} + + amlProfile := yoti.AmlProfile{ + GivenNames: givenNames, + FamilyName: familyName, + Address: amlAddress} + + result, err := client.PerformAmlCheck(amlProfile) + + if err != nil { + log.Printf( + "Unable to retrieve AML result. Error: %s", err) + } else { + log.Printf( + "AML Result for %s %s:", + givenNames, + familyName) + log.Printf( + "On PEP list: %s", + strconv.FormatBool(result.OnPEPList)) + log.Printf( + "On Fraud list: %s", + strconv.FormatBool(result.OnFraudList)) + log.Printf( + "On Watch list: %s", + strconv.FormatBool(result.OnWatchList)) + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.env.example b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.env.example new file mode 100644 index 0000000..68688ce --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.env.example @@ -0,0 +1,4 @@ +YOTI_SCENARIO_ID= +YOTI_APPLICATION_ID= +YOTI_CLIENT_SDK_ID= +YOTI_KEY_FILE_PATH= diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.gitignore b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/.gitignore @@ -0,0 +1 @@ +.env diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/certificatehelper.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/certificatehelper.go new file mode 100644 index 0000000..cdcb4f2 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/certificatehelper.go @@ -0,0 +1,175 @@ +package main + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "log" + "math/big" + "net" + "os" + "strings" + "time" +) + +var ( + validFrom = "" + validFor = 2 * 365 * 24 * time.Hour + isCA = true + rsaBits = 2048 +) + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) + os.Exit(2) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} + +func certificatePresenceCheck(certPath string, keyPath string) (present bool) { + if _, err := os.Stat(certPath); os.IsNotExist(err) { + return false + } + if _, err := os.Stat(keyPath); os.IsNotExist(err) { + return false + } + return true +} + +func generateSelfSignedCertificate(certPath, keyPath, host string) error { + priv, err := rsa.GenerateKey(rand.Reader, rsaBits) + if err != nil { + log.Printf("failed to generate private key: %s", err) + return err + } + + notBefore, err := parseNotBefore(validFrom) + if err != nil { + log.Printf("failed to parse 'Not Before' value of cert using validFrom %q, error was: %s", validFrom, err) + return err + } + + notAfter := notBefore.Add(validFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Printf("failed to generate serial number: %s", err) + return err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Yoti"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if isCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + log.Printf("Failed to create certificate: %s", err) + return err + } + + err = createPemFile(certPath, derBytes) + if err != nil { + log.Printf("failed to create pem file at %q: %s", certPath, err) + return err + } + log.Printf("written %s\n", certPath) + + err = createKeyFile(keyPath, priv) + if err != nil { + log.Printf("failed to create key file at %q: %s", keyPath, err) + return err + } + log.Printf("written %s\n", keyPath) + + return nil +} + +func createPemFile(certPath string, derBytes []byte) error { + certOut, err := os.Create(certPath) + + if err != nil { + log.Printf("failed to open "+certPath+" for writing: %s", err) + return err + } + + defer certOut.Close() + err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + + return err +} + +func createKeyFile(keyPath string, privateKey interface{}) error { + keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + + if err != nil { + log.Print("failed to open "+keyPath+" for writing:", err) + return err + } + + defer keyOut.Close() + err = pem.Encode(keyOut, pemBlockForKey(privateKey)) + + return err +} + +func parseNotBefore(validFrom string) (notBefore time.Time, err error) { + if len(validFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", validFrom) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) + return time.Time{}, err + } + } + + return notBefore, nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/images/.keep b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/images/.keep new file mode 100644 index 0000000..e69de29 diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/login.html b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/login.html new file mode 100644 index 0000000..ae22c31 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/login.html @@ -0,0 +1,15 @@ + + + + + Yoti Example Project + + + + Use Yoti + + + diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/main.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/main.go new file mode 100644 index 0000000..ac3e7a1 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/main.go @@ -0,0 +1,177 @@ +package main + +import ( + bytes "bytes" + "fmt" + "html/template" + "image" + "image/jpeg" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "path" + "strings" + + yoti "github.com/getyoti/yoti-go-sdk" + _ "github.com/joho/godotenv/autoload" +) + +var ( + sdkID string + key []byte + client *yoti.Client + selfSignedCertName = "yotiSelfSignedCert.pem" + selfSignedKeyName = "yotiSelfSignedKey.pem" + portNumber = "8080" +) + +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, err := template.ParseFiles("login.html") + + if err != nil { + panic("Error parsing the template: " + err.Error()) + } + + err = t.Execute(w, templateVars) + + if err != nil { + panic("Error applying the parsed template: " + err.Error()) + } +} + +func profile(w http.ResponseWriter, r *http.Request) { + var err error + key, err = ioutil.ReadFile(os.Getenv("YOTI_KEY_FILE_PATH")) + sdkID = os.Getenv("YOTI_CLIENT_SDK_ID") + + if err != nil { + log.Fatalf("Unable to retrieve `YOTI_KEY_FILE_PATH`. Error: `%s`", err) + } + + client = &yoti.Client{ + SdkID: sdkID, + Key: key} + + yotiOneTimeUseToken := r.URL.Query().Get("token") + + activityDetails, errStrings := client.GetActivityDetails(yotiOneTimeUseToken) + if len(errStrings) != 0 { + log.Fatalf("Errors: %v", errStrings) + } + + userProfile := activityDetails.UserProfile + + selfie := userProfile.Selfie() + var base64URL string + if selfie != nil { + base64URL = selfie.Value().Base64URL() + + decodedImage := decodeImage(selfie.Value().Data) + file := createImage() + saveImage(decodedImage, file) + } + + dob, err := userProfile.DateOfBirth() + if err != nil { + log.Fatalf("Error parsing Date of Birth attribute. Error %q", err) + } + + var dateOfBirthString string + if dob != nil { + dateOfBirthString = dob.Value().String() + } + + templateVars := map[string]interface{}{ + "profile": userProfile, + "selfieBase64URL": template.URL(base64URL), + "rememberMeID": activityDetails.RememberMeID(), + "dateOfBirth": dateOfBirthString, + } + + var t *template.Template + t, err = template.ParseFiles("profile.html") + if err != nil { + fmt.Println(err) + return + } + + err = t.Execute(w, templateVars) + + if err != nil { + panic("Error applying the parsed profile template. Error: " + err.Error()) + } +} + +func main() { + // Check if the cert files are available. + certificatePresent := certificatePresenceCheck(selfSignedCertName, selfSignedKeyName) + // If they are not available, generate new ones. + if !certificatePresent { + err := generateSelfSignedCertificate(selfSignedCertName, selfSignedKeyName, "127.0.0.1:"+portNumber) + if err != nil { + panic("Error when creating https certs: " + err.Error()) + } + } + + http.HandleFunc("/", home) + http.HandleFunc("/profile", profile) + + rootdir, err := os.Getwd() + if err != nil { + log.Fatal("Error: Couldn't get current working directory") + } + http.Handle("/images/", http.StripPrefix("/images", + http.FileServer(http.Dir(path.Join(rootdir, "images/"))))) + + log.Printf("About to listen and serve on %[1]s. Go to https://localhost:%[1]s/", portNumber) + err = http.ListenAndServeTLS(":"+portNumber, selfSignedCertName, selfSignedKeyName, nil) + + if err != nil { + panic("Error when calling `ListenAndServeTLS`: " + err.Error()) + } +} + +func redirectHandler(w http.ResponseWriter, req *http.Request) { + hostParts := strings.Split(req.Host, ":") + http.Redirect( + w, + req, + fmt.Sprintf("https://%s%s", hostParts[0], req.RequestURI), + http.StatusMovedPermanently) +} + +func decodeImage(imageBytes []byte) image.Image { + decodedImage, _, err := image.Decode(bytes.NewReader(imageBytes)) + + if err != nil { + panic("Error when decoding the image: " + err.Error()) + } + + return decodedImage +} + +func createImage() (file *os.File) { + file, err := os.Create("./images/YotiSelfie.jpeg") + + if err != nil { + panic("Error when creating the image: " + err.Error()) + } + return +} + +func saveImage(img image.Image, file io.Writer) { + var opt jpeg.Options + opt.Quality = 100 + + err := jpeg.Encode(file, img, &opt) + + if err != nil { + panic("Error when saving the image: " + err.Error()) + } +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/profile.html b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/profile.html new file mode 100644 index 0000000..234ae75 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/examples/profile/profile.html @@ -0,0 +1,95 @@ + + + + + Yoti Profile + + +

Home

+ + + + + + {{if .selfieBase64URL}} + + + + + + + + + {{end}} + {{if .profile.GivenNames}} + + + + + {{end}} + {{if .profile.FamilyName.Value}} + + + + + {{end}} + {{if .profile.FullName}} + + + + + {{end}} + {{if .profile.MobileNumber}} + + + + + {{end}} + {{if .profile.EmailAddress}} + + + + + {{end}} + {{if .dateOfBirth}} + + + + + {{end}} + {{if .profile.Address}} + + + + + {{end}} + {{if .profile.Gender}} + + + + + {{end}} + {{if .profile.Nationality}} + + + + + {{end}} + {{if .profile.StructuredPostalAddress}} + + + + + {{end}} +
Remember Me ID:{{.rememberMeID}}
Selfie as base64 URI:
Selfie from saved Image
First Name:{{.profile.GivenNames.Value}}
Family Name:{{.profile.FamilyName.Value}}
Full Name:{{.profile.FullName.Value}}
Mobile Number:{{.profile.MobileNumber.Value}}
Email Address:{{.profile.EmailAddress.Value}}
Date of Birth:{{.dateOfBirth}}
Address:{{.profile.Address.Value}}
Gender:{{.profile.Gender.Value}}
Nationality:{{.profile.Nationality.Value}}
Structured Postal Address: + + {{range $key, $value := .profile.StructuredPostalAddress.Value}} + + + + + {{end}} +
{{$key}}{{$value}}
+
+ + diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/httprequester.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/httprequester.go new file mode 100644 index 0000000..053b31c --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/httprequester.go @@ -0,0 +1,72 @@ +package yoti + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" + "net/http" +) + +const ( + // HTTPMethodPost Post HTTP method + HTTPMethodPost = "POST" + // HTTPMethodGet Get HTTP method + HTTPMethodGet = "GET" + // HTTPMethodPut Put HTTP method + HTTPMethodPut = "PUT" + // HTTPMethodPatch Patch HTTP method + HTTPMethodPatch = "PATCH" +) + +type httpResponse struct { + Success bool + StatusCode int + Content string +} + +type httpRequester func(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) + +func doRequest(uri string, headers map[string]string, httpRequestMethod string, contentBytes []byte) (result *httpResponse, err error) { + client := &http.Client{} + + supportedHTTPMethods := map[string]bool{"GET": true, "POST": true, "PUT": true, "PATCH": true} + + if !supportedHTTPMethods[httpRequestMethod] { + err = fmt.Errorf("HTTP Method: '%s' is unsupported", httpRequestMethod) + return + } + + var req *http.Request + if req, err = http.NewRequest( + httpRequestMethod, + uri, + bytes.NewBuffer(contentBytes)); err != nil { + return + } + + for key, value := range headers { + req.Header.Add(key, value) + } + + var resp *http.Response + resp, err = client.Do(req) + + if err != nil { + return + } + + defer resp.Body.Close() + + var responseBody []byte + if responseBody, err = ioutil.ReadAll(resp.Body); err != nil { + log.Printf("Unable to read the HTTP response, error: %s", err) + } + + result = &httpResponse{ + Success: resp.StatusCode < 300, + StatusCode: resp.StatusCode, + Content: string(responseBody)} + + return +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/image.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/image.go new file mode 100644 index 0000000..59badfa --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/image.go @@ -0,0 +1,45 @@ +package yoti + +import ( + "encoding/base64" +) + +// Deprecated: Will be removed in v3.0.0 - use attribute.ContentType instead. ImageType Image format +type ImageType int + +const ( + // ImageTypeJpeg JPEG format + ImageTypeJpeg ImageType = 1 + iota + // ImageTypePng PNG format + ImageTypePng + // ImageTypeOther Other image formats + ImageTypeOther +) + +// Deprecated: Will be removed in v3.0.0 - use attribute.Image instead. ImageType struct containing +// the type of the image and the data in bytes. +type Image struct { + Type ImageType + Data []byte +} + +// Deprecated: Will be removed in v3.0.0, please use image.GetMIMEType instead. GetContentType returns the MIME type of this piece of Yoti user information. For more information see: +// https://en.wikipedia.org/wiki/Media_type +func (image *Image) GetContentType() string { + switch image.Type { + case ImageTypeJpeg: + return "image/jpeg" + + case ImageTypePng: + return "image/png" + + default: + return "" + } +} + +// Deprecated: Will be removed in v3.0.0, please use image.Base64URL() instead. URL Image encoded in a base64 URL +func (image *Image) URL() string { + base64EncodedImage := base64.StdEncoding.EncodeToString(image.Data) + return "data:" + image.GetContentType() + ";base64;," + base64EncodedImage +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/login_flow.png b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/login_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..ca0e4754dbb6c6b9999b6ce74912319faa9534c5 GIT binary patch literal 104616 zcmV(nK=QwdP)Z~I$YI=?iGiQVq5 zuCDe!xG-GRw=e&}kl$9WTwRX)Ke+f0hMCjz?fdK>OeX(Dz}x>PM$vz89y~I>3?wwennRU)A$Dyz~lbg!9P*Tyqn-xgl3%v@QO^L z|04flG(!A~BJfLoR%Pq=|MxQ(c=0IL?K_SC9U1vA`7ib_{@*)g@!hBOzc$vUi%OC9Tw9(@M{>xf!qCeJsP;r$RfVGr~(hgc5|M{ z#T<4kt@~rm+Yk^*c)W`o4%5ZrcM2ZFZB!f+`-@Ec`6PZAu!j}Wv+1zOy{nZ>MA$-h zEqkyx*3A}!hlDwiRXjtIF7&VGx)50*|$@Y801GiXK zWVv5;TN7)T>92`dc+mJx0lSqEi(OIb5*l*Xrvqo$_rprKFVs|$SkK4eJ%{cHywi_cuFE<_71^cO34-(*oxc zTsQP2W)D42CkeFUyBa!Nf00Ev{6RQ4VV^Yo*rzU=a(`U)G_$HwpIjKM=2suHBVHXT ze#j$D3Ue)H+#qXhx zbtI0DJ?gq$Jp@TcNVip~(=+xqC zh7GL+U&|E_1j+)-kMA*#j`mWZMwb~wEWgTpjQtYMRBuKX%@0bRlIahb9cM%Pdf*Y{ zH=iU`Q~UMJSoMfTy%;`$a>o%PJuqZ7ng#g1N zZ@p#k_C6q?Ku6_*l2d3ke%RNn+FVH%UVf}<_DJ_W+yt*VFdxPW%njAkyBOujdr1q@W z#7*0|zH1xaX;n#~P5Eu7A8r?ONX#auQmz%u@uI5?8oE(bD?jOcZ`Jx*R9a^ft&0^N zd!CPp#RD#5>X52+kUEm~p|_F2rm*=~a3)TzEgzbvjN7la1y_MvkPmK)guPpXPb+_9 z<_^N(8TVIy`AvYGJwgbK!HQrV$;UOaD1W$vuvL#bwLf71uL_0Byuoj)G#y;f(PgCx zZa>vMwqODA2p8cSU0WJcs_CpIrIOE<kJt)MC+J;mu510%)OV4v z+$x?$uLny|D0x&QnDq8u7Ux+5>3(Spcnl`bKc7#V@Z(;9UzIbAY+W4x(L(k??A=__ zn1@bi31K8kqmaO!5wVKkCOZAKv4kQn{gS#{*6QTe!9C z{q-JN8vW7bGah{XeDX=S34TPbPyBw$oEBy>%XvH7zzW-x6wL|LwBS^KgtloLd+qPt zmc=kPblxhY=4J{wx^t^T}I9UjhlV(SMVB59PsQ`Xo^Mt&mlF+!)vUDJo7L6W#j zM*aI}n=TfDz-4d$yl?nFFP+{D562v<4yYz*(!$E8FaFxnkCk=zz$ibm#$xdm9SjZw zkWX*o&C%IJfRaV(0yhzi~*t-0b(%FxrWkXU3s|;0wAa zp`9fd`&Paf@X-9P&JIk8lAU3w3NOOhUi`axeZK3=F1hIXcxtQOaSE@mDN}h74`Sy9 zMGcm?qsxPgKJqL2>SGyBa5YQV4`myYt<(T-`0}aMB+S1_%(~$z3fKJ9@qC<3WM!zT z_&4t<{fEpcIS{Q_^mbGy+jH`u+ZKv-Q_uXDASU|OUhYToK925IAD{Nvb1+L6n+6am z)cutp09(mef>*Ng6Yp#Y=F*r{%^e*G+P+Hu%G610y1=T-+-k1T*#xO?w=mFoV79bf zm6jgAN;{k0R8g36*iWA;@K~mO3e!3+3(aEw^L8JM(N)J*l^5Y`O#itCT%AqjhA?0W z0H&0gx*K05)zX*oNfXk+?eK7w44Y|bP%$jIHr02WKu14p4{ZRhVj4oN4Nbf(5C`T_Hq z0*z$$b5$^;RDaOqa0ugJyCp>@9l8PeRDr2gM!7F+H`9Td)A*;V;-rKAD6R5W0{xVc z!uXz{&dJovQy%>{rn9z?(uPR-@N8RS67 zI!{2@Jefli-NRFp6k;MSph?H_C}$DnmGU?u6O02G8(VL4rHo&QKtH5o&vb1Ql_mKP zgc+m3KOf|cSVwmBlv6P%_l1$r8{6|>#eRYU2lyPhHrcz+=p2edAh3i zbFv9YCHN882!Aps$t86l7nAsK5~Z8gWEzIzjG4RwSkHs2EU}&7Q^t;?a@#|g;uy(V z**8aJmWm!oDai*b_%sLJ!(3~59iH=ARJVNxtIEvsLtP}p z+LdM@$6fwnxL`ZujoFiQI2%h-%G`g!iPbGac&6RN+OO7#!*X6h} zyRu;Ef^QA#Lsn-DBadG;OvSE5-HgsOK5*(MFjR6|eJQ^1Nxf8jXL=Avk?k4+LGa#f z-SnIBJd%Mf?1d28$hh7kRu$srbJ4kuza|nAR=+wIx=3bX%!ynQTFTB0twliFcsC?( za|y#VEIbZy;(X;eXf@|>-Zv&7UuIs_4a_axxey1{_~2Q1n9Psy8jplf5L!=2HXg>U z8kZ!25s3vHA(}zzMg9sbAx`YEYlTd3aucN%l06#-5-kYy1;tiwr=!^f2e_B_arn)7 zBpl83T>D(>YQJ}AhbbcZ3%cEFKZj)*%==g`SPXj zomxOv%HPVjOGJ`h^__8c; z&`R<12M?6Gupe|Th)d>HLKOM@fnxx_mMZ|!Q`~h_Nz<<&KL9Us)=J?R0N0|J!C#5u zYV_f79W%MZ1-F!y-Vbj!Wickw!0?9$H8tNd#aZX9%bv4rGyWZS95sh`$1oggMiEUekRO(Z|9_kFDr+8F`qd_R`^UM@PN?zCFH9meJl;m3EkVq`J)k(27h)O*CM9kR5 zk*R(OL1w+gY@a~P&s<1`N$zkj{}5+su_be++qW_! zW6)^hgeN(@Izg5yp1E+B&8ZxpvkHJ~^%L8RAS3iE}VEfCG*yM5Ar$i==ohz?*+%P6D%Trg=-DmjV~(w zTT#%-u;O(p@bjjt9AlL)-%AZD_;C$zSzfIzs5-R6%)i*rZ)3i>YjqC005}7_qZS+F zvE(4Bm?+jNMt(s*mE4D{pk6R9MAeu2cM=heMS&Myj*Jg}Ew)N9+u$tpj2be;S|9R;A@p|pQsK!Vi;AD+pC7+{`4it~*m zV|RVBAFRzj3v-T9OwFMIGLMIH;F7$uG_3V;X|@KNbJ^Us(!*C|%O&~K#Gy0llhj&z z{WkkZLdkVFU3RVQWkD$8nk4Q4aPi=Z1*m_tyDec1SscI zrN7A2Y$q*IsrpHT_FcA&IM=MN`k_u2l8Wy$+jIO<&Q{}ctUQ17o24L_;sRD;wwsy_ zIH6xB^*-zf=MG~&k@e=3c$fprl6k2n)-pKRx75WMy9wWW+@gf7_WYxs)G%17;h(ga z4Io$J`>zPHgZYx~T?nVUvXk0e{DNS!A9k6AVcvX}C!?)(i<3WW{ecm(7c%mG{LYqs zfb8Ob!5#TuqGJcI2jkaP(z?~^)Zkh1pi!PQ*<-%|Eik{oMPg&3k6y3UOeIV8sQ`KN zbGmCr9!bI2wt$Qjo=e*AxPUIPjnwm-454;DY1@}>2t7*|#h-C0gxW8(4 zem3z6Z||IU0%PZhnOE$s4_{Qo-y12-2~r)R21u@uBy{VG5N$d2oztgiL5L{`jk_VpixVP;Cnmwtnbw#-^2gh9~%P{ z5`>)D=x1}cZyi!v7kLO^vYW(L#qLS6W80_@-MQIar=ln<;2C&;;hd33>9|!XIjxsN zu8|8cx>RGD8#4xbXw}g|cg@Rt{C=xm7}`7LFX>RPCgv#N)j-CH#!TXsUeBKK;qb{m z#PHBUA)3i;LEB!4j02Y7ib3_Ew`_*L2?h`QelIl2fi7-a$^iQR9f_7#aCOpqM^Fi% zAKs22GDzer9wpDwj}5!LAKLixi%u}rLgNONk@5Uty`FRTr*97PfzHj~QE8JgHgo(= z3j27~A8MCWgT6OS$+{&pZK&L_%t&|v0&_>W2Oag=O7x~yQa|9Hpuw~T0^KFmG-obgxEzg7$q zG!2alH2*F}hSFKK+%pUi`4p|ujE8I7OYvby85wvuKH_6$>?>H=chzg@|IM&dh_>c_H5sbMm!uE-m<|S#!?Oa@YmC)NmXQE>=*+StCg2!-N|zRuvV+9_!T~ zI*@usGPtF{iCee^L+EF?mH^fpfLjRV6{TUQEk9#nnSvWRQv;Imy#Z9PV~$^eNZ6Az zmsjy5Y>nC{nHMKKj8c^aBMdoU4%(02Lb2^``qNr3Vh?}J= zmf56B=-_%B4g0&GL(zzWa2URzcJ>GqOl~2QDT;OdPqGdhAcjPw^}NT2z)y)5U)Gg<#bT3lM_~P*46!1Snl* zDQa_odfB$*U1_rH!Q^-($ebXyoB)R}H!qeYeXP_LI5}>mV}%@=P5GSr)w~T*2jMGz zp+EtqiE^i7(VpUXdkgJmdb5~!Y!xzPl5>2SEYkm~kjckzpK{*v+qI|)m{1J48G|dn zD*q8KGm_{qR`G8?cr!^0mqK2iXadql1CGN?H9wLpPbj!g%l?s32aGi1`#Qpk58X2E zAkH=Rc`ug&PH8x2IE1EGNxkg8n~N;&^Pz~)P_E)SD?{XzjC^kbKHhxwGf{i>`)=v< zQ@f79$mpT)xp2|MO;_Jfy0QNyID5RJLis}c%Zb(8wPt5XgNXdhLypsw+JmijX0>UH zr`a!Z{>+8*(#wc&(&CGROWJ7zN$P&=@8$4|_~rIrfjqf$p4;z@aekmIE=M&dU3>}eH}=2i205jaQa`}pp_(k&-fDG!<%I9W7B7NBnt_* z$?Zn@b8Z7jL}ZKkK4`OPC+}U_M7E00R#&Q6NH8H8kK}p!xSXRhFbAxz;Ad!nCe_V% zEWgcZ`>{Wst9Sc1BkVp`IId1L^d2<1Nph6^>M4{#_;d+OYSbUz@8Yl8FZc<|!!Fxy zx+SXXaFfj66-<6Nhh>Orh~OfKA(gQ=69TAg>Y`=LDe4`dNYcqG9~mH^Y``XDJLz4$ z$`5sxR)ai0y7h|tc%E5elY_4*{EWJN|7a~!0*R;YWk)K-O?oV3Q<;68gwb#A4NcYo;Yd9_$Cjw( zooR|koD)bJ>z%h`B6!7p;QAOd4lYN|N3jOR(T7g%HXPmC@-GWu8RsUBm*{>q*-jfg zHhpK=g%s!`Ufc$3(Yy@D&Y(Z?jpcm}DL?@aWqrnhp`5;@&GA$D)}K%12kVzcL!_a- zsI7e$3L~-4%53-7xC>?OkIuX9E#TO4fI4JN)et(GI(`&?UYc347WChTFUj)-{9vBG z998wSm&C*_EL6T=iu3Mox0+SWse>;e19%w@ZS0T%)7niGoKy?5(999* z>9>=Tc~d<(ntvzEem!|o2EZW6#{Z>mDy(P>pNw1^D zvXk`Y))nH-NQfMwRCw=rm49&JnMRcS>^TUSJawTVZP1$(e|p6?guBrRd`-{ryU@Ks zz*9t$pmW48Ma)eGTrUqPj<0xGy9;aFCBJrU_4ZJBUgpSxOqfN(H;r2@ zWb*n8)Kqu@y_?^jRLE=*2$cf=4k+jeIR?+86YHm6H%tVbI?lrpf}i^v#w9|GwDwvLIl-PW2E!RZirXH~rzP_ghe(u_@F<}ARtsy{zRWX$Cm!hce z5|09u@3X4r?olI%y27>8w6VLJS>#=JRve24C|$ zmg47*2YNDwwg?Ktx+tyJX;ykr+BB%-H?tT4C;&=_1SSsHdsl@z)80D?=**ilyJAfb zG}jaK?S6g28drI;G41Q0p2$jVrJ0xgk|vEsw{%pztZ>tiBX%5l z_>s?Pi-VJqpDUAauW+Oye1S??5WIh&s1M3wN61lAl&60LH>|%n9&uma{+VkqTi73i z1%)|WC7PQkQhQMrf_SR!@q>ORQHD70jpn6zmUu=+>YZMeBqMc7yPT&J>I}Q8B^Mhx znT+`megM|VRbKMY^FOBhs_vtw`B!~AhP<5`w|Hal^ow4-rJeS0@iiGKe;+)@^)u+m z*KWu>W&`IJs#r-|2gCA9X1}w}P;!a&Pdz?Pi4=F#;zH(&cZ7UI@_D z5xS0BFa@@x5w`8yybg@mpd|Kf6VQTk$S3tM-n;8!_Y z$zWwT*F5lPreL2JzvReF;vh>=xf?rc@m<`yT%Emyz<3BF{QR2a>W8X6w^xMkEtEPx zZLm|$_Uz^>0ZDm*+@N^28T~PTI_Ew=!^nJx(rMibT_W&eYB+LOa7lxV2XH)PWPEAr%St~1+f5Z;4?!`K&0{Vx{UY$~$cT-iZ zY=kMQ35di^S4XR#f9$HOr)|?j`19fMhMx&g;ct@YxJr_iQU#*`wZRt2Y`OW(7PtS z=zuVG5(8)l-_BqOAxWQ-aGh8?tTp5py;e@Qrpeil_v#||12}GU5(c}IP>|wWqSX_^_$>uzC<9Q= zDr@whF!yl;&yWH|1@`%4O0;6XKJ%Tl_Y0gkkw(-f8QDQ-17QJrv(USkm{Wvk^R&E$ zPS;CQak#a?>!j4$M0*onrTP*9u|D6wCXpTfOI$P1bySboIJftzH9n>c_+9+yY4f!> zlA&%TG_~$xuF)rWia`=<=|QooUGcb0kQ-U|A~7H2V?G>u@%vaaZC{i7UfCY@s)FGq zp)UB5851R$RHNjL{8z@|`>Kpk75lBN*qp5hYiJhm1%+{G75~2R^{7Hvuk#oJ9+QNs zp+l|_klX^TYXbCuHpU7?iebyX?7&ggaanib0pytLw_62@B6_ifbfLPRL2=jb_@12f zV<2BYoPC3JYzih%g^29E_Q7dpX;)c4(KfJAA*u2&T72%;W-gkZy=c@>g;ys2w07J_ zJMnIXQS>11k3DR%Hi}Y{tW0fNQBNCwA8=9~b*jND`b?^Lv4*b$Ns``ek?sp<9Yt?> z5DE9oe)F@&7{Laieb1D%tMGf}wmKrjvNkA*R~r8<0%c-J`7d`mE;&Y;9tD-}_XZiZ zC?lQ82hz#p-SHFMn&R*3&)lmM*K_;HR-eH$>`WL|CwmBM+66rmF$JdY2+R9G@l0Mk zo}tmVZxl00MBV%d$w|Im=GYF!RT59b9gvi)QMbE_RSmgE?;-MQMBWDF5C-paIb5+BT4e~dVI+?U>>eE7b8c0!=>&-{^9YpsAb8EZy|db zoe}=)1|iNhdE?TWCLSaJyidZk^?N!^0h6@#c4tEb?B;fJt-&TKfDQ^RNa5?mKcR+? z0UyegVm6bqpTM(X9)!ED3K6do#M{M&6C>t;M*t*VTYhq0&Igu0qjv93qxkU^Hz?KF z{yu1{HSdbwkqsdIUzQ1r%AIwqg$orOaPAIJvH~kl%JP0bD#vY?hj%Mj>mvPIBU8CrxOC&W57fw=O-8OzZO>Z}T-gz|IG#C-177vX#i`IgcqfmC z>pY{P91X@vO2Zahown@6dsJH}i3&rLpO3;WkmO&sx?uj8F>i5!)2A{g%+UWN92i_n z3IQ}pg7$nRUu2oX@!M+CS*d#HPZh?}bq71Fp|pC%rt0bZD8=qe`kYiq*|QZ8oGXfa z?f7??i#pd)+uyNi^am8R%Q)0*+;P3a6^g(lby(?!?vw9+?1-uvkb+LIfOgg#EQ+G# zyk7kyOKP!O(sSdJI2M^%>Og(trfpFB&lutmBc%5$lXfzI#K8s=&gpR91a(6$9-?;f zW5o>4?<~va($603Z%r_|kkI+^29hh8*D@Srx@6OrqS2}wi~Z4A5VW|*^)tu5<2ayr z`~X4azLtXlPVcWsPZko-CvT80!h*btrZSrqISNqmOURA{#hs5Q!5pm#@dy8fy6IOtB7bsImTODpyS~4zll+MK4Fh+sE(>K1$brw z&!@!vQh&z-!kd^hJ{`Yz9zRFU2_xZJV01dQeMA%b@lMBn=#|0X0*Rv8q@maXsJ%+f zhmfQi3&%j|t+SaQc@z~e_6_i5^Ps3~f7qAS;+q~k1Oid!NxB0r?FJLgkglaj+XcxB zF_1M$qbiuJJF0ke>(qFtU!c#;s5E%7YeK9*2%BicZ&Br#S;^R9JwxP<6WmE<6$ zV!xkAzzcD?zdYJaV-^%%dB4^m_^Rdmi*;dKh(AJS_$d({3gn8r<@#6v3VE-52736a zWqcrFgAs1^fbcR5b>}lX>A0yN_Ymv{axNx5G00+9!#C{+6I2!uT9ql~8uavN$muz>TiDSdHzq)^`^B0Uq7|`{Y^3jiCsg<{(N@}>l+5^+1Vb%~kd98;TSC6>mixUNFIHI2u8DBX#vU-0JkqAdw;s;QVj zt^ufTKRwkR{`Vx8JAE2Oavj>UlHQktr)66pUNyhM`BXC-?u(;8Sb4+`v}8JkjWZ8R ze9yR{ATff3G}m~{j;MvK0ik9jBwXn7JoD@iQH7X^b+wq)$F5Xno7wj%+U!c9p4h=< zc(fmhCMNXMENyLvoC7MEfxywDa*?CoJH2l`Y^|#{pUKK>=(Yz6dB40EoGiUr_!G1e|ylMAeRzo?f^{= zV}`Ho`yM<+KR})d#0u(W-JD3*MzVh!t*(C2O*8mS8g-Er`?!sSUQ~p%IB0U_sb4^@ z=i=lUny-c+%D(yLX*pb71V_fM6;pm;l4w_Su3l#aV!3nS+a}a1Hs%aRL2xu&us-z# z8kzS210JRe#@K+WVexsTvUdPM4`;3ib~Kf(1|Qr+en(IT4d|6a`j5}Fq#3;0iGV_} za#GPD+-;O#iZXK{T7YEjZ&sI23{v1PG@qFK$aWjNu39+;5eG_`OmKKXV%hDJ`C!;- zh$rBbMiGxHYHxg$6BWq|&wwlkavboV+Fb&0lyrd;-8VV%ysS49P?Lv$$VU$%j-auH zjgkKS<$xk{Q(R41EB25ZAI0k-j3JeFw2sr@LAJ))juzCVT%jp`S=B7T?)sj327RP9xN?efL{PL2JuGoWde z=&nestijy6|H=XTg@SNieSpvoGA3pf`Z1%Kf!E7dLEDwo&!M@ z^ED}ENHpJs1$9PO^HGJ_-00jSuLg?)DiMaM5h=>CU;cFTEA7ykWj>DQK;7hJfj}9X zQER`XL-)InNJ8$$R?9~lzc|TP8519*1{`+l#Q_n#p^QA)H9)G2Ueav|tE5OW zRvFF9_q;Btq0^#aoEczM8CwpBI6$)lzC(T_UkxMU z)g~SM+t~sQbZ)Hz)eiRlYIV}JKVr6-5Y4+q?W-ZMN)s9j(ET?{&*g_C{ zSFlQki@2MWql9&L?5rg6ZO`V@qvfMo$wYj)2#6p-A9eoX-*PP0zZE%IeG$>xt3loC zU2s4J4BCgKD8I8p0XkWF1zJ6os5fUbFP*&HTTXiMlptd)stQ>H9c1gJe2%C1Q-6xv zfi1b$Icx&-nWeQ&I03Q}I(Av?8|dy}Csdka%a^jMfc$Gx#(y~n6?~-gYJ#hDW4gOQ zBY}f;ffwB@x$P8Wu|kh?YsM7A3GsL;Yzprg>m)(m`n7(O3e*_$DD$XL0_2?L6K4|C zz+jY}20=Em74=UBtof!+s=_2xWkbrz+4_~{xv6OAzrC6ThZRx9_23M`6>Cc-;7`eL z?1N9|=c(1g-h_==BkR$A>*EV!a)2UUAo8;+bgi^(V11RE;456ylk+Rs0+?V0la{?1 zpg_f?-!a1J((YWzb@$z4KtBl0chx8Kkz6zXf{gNu@5X|-H2cIzKBhoT(c+bE3llNp zul|fOBI4)+G_e*y!8j1|n5Oqr(Y#2M`Y$TzL)go8G}cow-!tDkd$WxKtEz+HvRps& zWJWVBifdUDjVn5ETH+z@fz+U+0x{ueTDsAQcX->*d)AcP7~Tj9ATQ4e82yWXfYQuV zRhvHmFKGpqmFv7JL1%jK)O0Okoq9O;ei8V{*MR*oT=Fewuwd!caR?zP>(B`VA|eAa zTH3?7u_FA)uT9!O|C558pU4MQLIEMATUdM!G#0^%UA5qR*#3;pR;fB8YBy$nhbt)I zcmO!!dsM%KD+ir0Xyz2#_zH9cgtz%_v9hyMcb@rfqmoWU{;#Xf3Tp)9 zp=)bBv?vr0Rk)i9)Ml}HAjLAa?DskptX_uN?$r0UhrGQedcE7Z#q>&F#G>XAAETo6 zHG_}Ql?-mnL5k9Jh|weX)?5}7O`q@lY>;FI?P{la0S&s%oUws}g=maHe8Fh{V}VBG z)!r^lst@SYwM)-=k>#7t2}huN3cT-E2POXC-V}kcIJuTV8-Yp4>&d9)*0k>ShE*R} zTJYeW418sGg-VUfZVWW&4jpCN*HxE-y%xB_Mf$5#f&L2R_!{)-;gPY%iJcWNdBrjW zbYq~|ulRvzS<9!*jij$Rj?r`YC-PwLyG+YC$dQTZ{5C(3JA$6AD|}u)u>%2%+I?FR z;7FYI#52=5pkH_*dn2jVL9?YdW;NT`!X-nLED$@qE}OGbZ{#+=uLt(sV;qo z#>&(@@%g$i&$HERY3QFV$?0HFA(v?ndnhf802;xw7bm$rQBZ~2jlelQgr^Y^sB6+T z|4w<9l$Ed&{TIC{h$JEoh)MEWUO|hmSAT_w+t&~DH=Lpx#N}OX;g8;|#nE#J>R6fY z#I*`It`f-{EG4{)wKPp3zRqjV#pn$%7HFLi(-4SesI=xmf7C~tS{H(A*#(I_XP}IQ zye{^=uyO4SOtw?>*bF+*Fyt=)i=CyIvbBKpiC5IIzXV18D}J6MmD%UvH6?HkG9PksOncw&u5gL7SSwHqTA1#t84Y$@Ma8 z2JnugMiP_>5Y3%130O|_O?-W zgzz$P(DV&~qvL|iT-DRBn)1l<;(X|R^MD}gl=}bz%M!CEhYcTTaR&g~*NbEyBF#3p zTASn-_l&S5KW)jp7tl+|^Brm%7UOo6BL|HAC3e$A;;8mm^i5qK{D%#`w$FkHx+y2nYh4vKjtefE`rpEa zSUD6e$3M`|1$w|;J2TM)6y`Q%b973GjnOL~n%uYqB5sppDR0IO%Q~=JG1=5u(>UC0P{ijyyCX67(6caA28HT2pnhvc z1eq}hEB8cPqnlJ85e2gG%!qG3-x}UaCUv?nfo%G0oUr(vtOGTI-{ysX9Q97XLbwK? zp!T=7cFG^mS9GSh=!G51<%xAsSQZ=|Uz&ZZ3sN|1(u36KFNnQY$`<&cnhV%YW#~~#chAYx1aHXvEs7>)O`YIw2t2ThnE1^=YkHk zxw%z{%rXilP^oU+5&(whL+mUk&~3fv#Pq&+AH5No=prujAa8fVpbvJR&_1S)ws$^wk-w~dbqh&jcqNf^ebGK&rq)(KeLjX# zRJN3YUjQw`FYMcPpmSB1a;a+FNMPTu(EgB&iY?w+nA{^+8sO5~`Gd_|^~yUS5>sVt z%)mlXa{qmX(4CT10TR^UL-byKw2ik3~NZR5c-?-2$GtgBgkOA)r}x z^}y-^C^CH=GHQolN`*69huv&dLoyjQCE0wZdms61peE_i!#E}qMuMvM149`lcoqP~ zqRjBA1$23TwP!#yDr=$2yZ-^sEwo%lT6lUC$_dErBX|Em_X#BDGd;u*_KJF1MH4eY zllko7Vg(r^V2+akV(J^Va+^_;MV0SE_Rlg^wSaas;UE$Wf8{zwwzC($&+4b%00^cc z?sPf|s)`GT5v(_Sbd~_3e3;rxi!o7|Xn;nQ4v3e%!Df4O>ap*txPSUo4>>X>8#44H z)xjO{9-G@tH5^$Cfbb3SlVN969xZo)q3e1imh_|dv& z1Onzy3sOhd1<3*+p#Hdhi5n+^54?5xk_>vV7@#nGzqz^Itipx_zQl-YN7W1MJoae1 zlfk$F1*jMJFH#YxiLt_@L<3kfZ_ub-! zp!;paAC*UMe|OC=Tq-k9K*O6666ozszbzb;z&2=tSb)yS6k_) zWzJ_az+S+ak5e@m3ng;}vU*NlcgJU#{O>O=XO0Lcq?yZ7_G}eVVMDu&2+qU-xjv{s zH%7dftqcC>j;> z!6qIz(;l`4IKJ&-sm_^(`Eecu*~1@@!?vQvVsEBPzbj8-CaEp#W?HFc*T!6i7w>v(fu>kY&6jTDHA>>Si6@fVPX_6DFQ zer%Ar+pNf**xTCM`r&(|CSW5E)!_She_o6chlFd@gP&0*%vTKQGiekbNch`+C)L6# z*`Q^|vVDqr`ft-JD|ol*&iTIn7h}hr_qAJ==i`Na@T{As-St|JFI-q>t@p<{``nU# zpo2Ll>^dwpQ*uX7Ayg+cKR}JlrKuH%b{U#aR32=K%n5p#KtKc0zrn;+mT*+hRk>BB zbRATL;mV;`f^Ac9(b&p*-qQ<;Z(e#*(&E?YU1fgRK+bqr#NX@g+mNX#+2)Q>i1@8P zizlCgq30^vQa%Ru9>G>Xn`T<--EI>?;}QjUZp|iw4h`sGlfEgspOJK+C}!2_*R+W% zKa!`;0iSGn9n9_`QEzqXZ(EEkpUvN{{K`A!OEX?AZ_hxt_YK4fP(@#2#U|2h|1x~2 zp3+UM$s~(83@62GURy!8$tfWKgXO#bu5d1kDCfZ10|QNIx4$ftct>=%PMYT8VIu&q z&{rEaXkh;!vRlYsPmllu=9ksXz8ig6vj^>+1_P9aZ1dJ6e)J=t$DOg9QyoF-P?_qY z^6leI%DI3d8o`m)uN@R!-%8i6N31s-Z;Un{fZh3ww`HV$$K=_5+o-`k5#S8FPVKL+vqLhQpMCj*J0Eb^lNBas61N8EIl@{#26g21oTW)g7n=p1 z)gE4Mr1LMa3nVeOtb0w4vO3fVx9#{$G1z-h@e?ws;&{K2H|Rm}9wvLcobi?b4f@Vv zh5BfKRohESH-HcZ#(_agi)L++?8e~^SwO5FcOyi58{4fZUV*x`8P zc#lnV@dd`{XbfG*(+xTN=CdQh1(SnPyB#$Thx5h7P+GthMHKi5j6$HSjw2vlsMJnu z295lJhNsb>-m%k|7PTK`S&cKuEm9343Y@Vy=_~a>zWZ)V<-JqP)rR#T@>Bu3m*=@_ zyt^G6?Rz?L{W~IC*ovEbZRq^ByNVbBik5wEa5cPAlmdIapwI;>_}T#s`uNrX^3c^# z7J5DK(-8zutwC87w>Ho!$)Y+V4Sqx9*H{g3)Qazu-K__DAwBH-Dk~uCdYkkJx|v?3 z)HhTkXOi#+$|5T=V$=Y&b(GOTCDK6vu)+5ky$z60(=(eqM<9u&6F$Ep1wh;ClOAwp z02~x={`;uWJ8yMb5e^@8YnVW9_t>q1nDaho4Ea`)?%DDX1`#vX?Lz=t6P*j!QTeZE zXkEnxNr;s0+v!K^wxC_R7?WfH!!DQKn90Esx9bo1FBP*r~rz!vF1D56kb z%#mhLX*;K@p?Ho8g;&u3(C#zXZJ4b;&$9T*6Mo|Y&71SQcUn0zSg<$ic zfD13Id912F-}-kMX+5?|8|GevN2dA)`X`D}`+|MiRBb=qoAOQ-VbEaKlX>Ws#Dm58>CEW$fBPy&?20 zQjz0vjzXT}hMSkjAJF+JVLksMBe+AskHWsjqiK2YXka@EWRlF)aYeG$7ncD$!omHr z__wZs&Y?E0b)8VbqAw-hSiER`mr|1glj5?m94y9Sqr22SVu_u6Z%^WUGFbJY*@L)BYVW6m05)O?FN(P|59 zwyKn_q(BLEG+E5vVi*c)BRXII(kmw$@u9FypIj}9*C>P?Pd^@D&EYgv%*1C2e4_TK zuBVqfaoeVDmv%}|FV_f@ArBvcdj-pI+9O5x$#*uZUtNe9;~1V9XG$=?V-(yZcQ&54 z561sC%|w$qu` zfEG!JJNa@?1urZk&{F8yvV#MCRt^|TvML=E68!MVl!tX+jn+eq;!_O99ulMDNohPQyagP$^JmwVX zlDDc^O0Cgxr|i!TYt!SU7PnWb-zG5~RHi@ja}yrD{sE0q!E zxN;OU2Xe1`C*H$UT3tiCd(K1a+AU7wt%iQR3UK}a|DMlj?CcSdMSj!ZkHcVgNyguw ztDVRvoFIdIfJ7K~?q3IZfZq#6ahtqfMe|c#^C&f@&39M65x;;Ad>Z`c|GUPAJ~ls z6_OOW3#BQS#=;=9+RA@!_5Mi@*QgHBoSJA&=6hCet4%@)+qjpf?_?! zy)RPjCdxJa@qI)3O6?;bMulvxZjndK3eK}5fHAj}O z+xqNKj>!uymb)<4$oVgUf4UnCuNHoeC|Ag>L@bvaJ)g@rS;#dq{%slA%frMrX>Nsz z^gURMHJSQVLoo-sagA`~a}v)<>ZB>>h2)U0`e*N3uHp;oKKTQ4^nyxHx8?oIPluBu zv|mZf1`Cw}c1+Q+?_48kO{#p7_c`9+ZxH&`@IA+H_v8z3w!07ZR}%HzX`pdAwqctj z#*c88UB9Pi2QRugT*FX@PMK>xKFp@Cxxf$Fe~Dc-5;uKk`9Z-p!}SM-hbS2*W&ntZ z`^eI(Fl)W1v0RPfAhuY!&ys+5m5ouWS#qm6nUwAPsN2&NQ?5yy@2^Yt*Z;PYJL@+T zqD|B7{M!QTt3cty2tIM6IZ4&Y2snV@|1KxJHx#N!GCCn;`v&Y^oH{_WBjd_S8FZ08 z_0AuhMG5!*nTq#|^Df$8Grxkw0g~KeT;81Wc~7JJ{^V8~!%_oVc)v9^7{XoE%lQ3!kfF7VCFQeyeeq@H`p{GRCPNR(Z zwY$BWU8lG{TvnAmym2V8)r0|DJH3I=AQM97<@KD@3y%=<&-?~CJ=U+Mr6w2Q)~w~` zgoOX`K~F@VuYzjxkpUYyB#53K6Z1bl01Q%f znE&@*Xw%bgv1+S-!`1n(mj1RI1|OpS&({CxD_A}_C@D{#cZ^T@|J6C->vJZ@|9+sq zKM|uL%R-3z1%A+@{lA}AdML5-|N0%FGASk|OoeoYE0pv<#`b?2MhpV||L=nct|4?q z=0TAT08}I40%`=}Fm#Zcar60usuXYZF?cYftpF#)JXETtFMdDf*1fDNT=1WSL5!1bIv!YTC~!73vznD=cERCh3n2?vHP%S zHglTUiP*79M+T`WvxxJye?if56%!Xj3l|`qzC<{NqNTl@Z%pHZ0w|h|w*~-|0J}(V zBugiO_4fcm+Od~CzDpVKVlClcpn&A(OEwZZSSWdOE~hQSP5sk2(tH3Ln<%(|_OONq zfB?`8Fv4471Eesf08^e5<29O42eAg(0U(XikPeg@OldN}!gIj8rKhqDpzc^L)ksN3&$QjVU1sT{(W0@^(qSC08IWuxERej*I70hY zjKy|-L~ccdeMy}PSLa}-?-1b2!`f6beY1)g1$2 zt@E4dzn!^tvmO%tlG1nOfeygpT`)1)1gS8Tf?Pn>dH@$>?~;x2hVMrGt*wjnhfJ?` zUNR@hA;F8s`Bi}TZbX1VU)rvlS`n$_?jITOq>mc93&eAxYKQU5kaCDnOXEdF9`Ng+ z2zw#C4{LNf>VGkmim6q#7E#&hyEuWz8;voR+QS(>8}Ff?269Ci#>=nv^&K0tz?zGT zp?N@7d^4C0KLuqSzYm4$}_y;PCm@a!Gapo#v` z1WoDF=%fHhac=E$10Ef6K7l;63 z#4X&}086@{UR$U4x?8j8v~%q%nQH@hxVI55tlpbX!t2L z7H>4>Z&oTMXU;j^Bv%v8fd`jLsG4c2KsSiJy(C3>bGH=$M(@&Kp42Oe!Wq^y+T4;s zn&X6KgI$wcYc7@tCBY|g zV`2a}(UkgBP_d=@V_htQ>Ub?-OOPlcIx=mimJTY7DKytC$__Bb?k-iaggeCdcc0RS z2=wVVWE*CEl@pqg>;j(ar^PS4_APUuz>0OliiIB;{LU>)(RgqN1@$Mpzc;?`=$@aP-MJxVZ#GnSQY#)e613>qx?Mm24Sk5fa&T z(La|PRW39tv#xzXtS_kqFRx67yDlGKZOV8q*L6gtCr!CReO?c^lqfY@sf#TYB>=jUh zUx-JX5y-5p`FyRb&2?tNQ=K_7JS-^>k60A?+SDSO|8ets zA!Cp1T#^IlE_dM;>-P&1?KWjdl$cbco^82*U04Ht6w3XVn=!s8iAqo6V@Fv?Azp#r z|1zYXg2*1%yAipz8eAYLJp7uy&&gB}Es)^1a_N;%m)W`_ zAd5v7ApffD=5Xu?{I&n3kM)G?jAWxrJRI?oFmZ{Y*X0O7ZD9`y(Rr_i$QY{q*8y3D zkc{c2vE~{)=hK$%j>7BJCHUwqtp! zZaC^VF^r)bTNF__nkbi)!5x+18|-wG$Z$Cqjr5x>^8uCLd8(fGdR7?j|HNolB{MrGnS$}= zp46Of_!FrS2Yi(pXTbVkbc_k(H36Sj{3}({V}fW=!RU(oOqmMWRBQGk!jB$cma}}O z#s=$G5bwQ_cm~Z|6_4g$ThS6@>z535EoaQyg%f}Ww=&Ey1&FtX%SP|D_ra^Wev%I< zz?TU#ogrKC5K@7|!jXa-*t%k7IE~v~va7pOG+gVr&{(#oTi54WEb{xl&HYjtUkFtI z)n3vB09#<9!PbiAW05-IUU(+=m4yH9$OzkW?CjJhMePFYFz~=CsW-TH(R7Wfh<5N#w$zC}38cQgQ?1H4wi%Oc(*WEkwq$z}COL z%aeSKJKEg>*Xhs0Rx{d+js8ov29OApGoy(Xbc}8h#98FTYd`wmOOV0PH6|tuy_CuK zA<`_fhbrpG?)o~*R*|`%_uhjS<_^v-*)UC)t!Sf_l71=&T3E9E4EMXFqATp)Y zpDFvcif+>qz48+%Jcg`xjbD%Q7c08gN2Hi+_P>yT#LX*gj!!BEmBhXJ@KSt6Fu>g{9K{ud5Tud2-#7D05(N<-n zUkjs(5JyC2VBGIi)DT~dzxH8i!YB%aVC%yy12a;!~b0} z(7A@{Wwup_O{+Yr@D9Y9c;fbjdZXI=9&x3Ca6i)sAv^EAAyOAW`RwVee}dH{m1I`I zG}WI)9V>js>);lDBg)3i3)0lvKGYRnZKXFg>Pa`)FqNbk#vI~*$x31gWObd9crdkK z3yb?2&qPeFDbG4sQgE16bL#{!RKfCR6nX7qW^DMEv%9TiU#-t-Q7V+5pI8CX7J`9z z?l5(I_n%4ccowkTuWBZ-a&Bs$vt+*+r!u#r8)fec`#k!qSDba!YG&mfddu0HL-S4rjMY)V~j3X9PxaL^eR`#5&7F-)3}1 z_}FE1hKt5B#?VBglhpIM;H6gbwLk^iL|*Oqtlx@Yj7IkHx;g*UR_(kDt;u@l5s9jfAZ<&&Nf8manGaC>l?GGjGbl_YhKkDG9T>4GZOuaZhv+;NTxyKY@h?XL zfxA%$DRbr*V{>C1!lNJ=b*51rP>+$Uvx(&y#>DrR#u^ zG8*rB2_0sE0TNxHw347U=*FHzudo-dH5blkYp->oCXU)~q4@+#U1 zD7el(NTI#-^TjYus?l9!Fa;KIG`;Ydy6GYvfaGUNJ+J*2!E+nDz|2HmhUa;KC_y{# zK>pc&S9=c1x%IfhL<8Y zN1^7hT6xG%pqfoa0gEiNWZdIGaWjaEmUDO|kHdb;KXvV^LF#nfgz2HFA>7o}EX9-< zI8LJS>&=zjrs%iEYo~*4!klqzSTw%xdF8)jyN?`scuvwYoPvuehb1pL#2AltmQ>xy|q-8%&vl_vh}FrB@V!;6@dRh}(DZ^n{-V z!k8JiB1;{Qgzt;=@8v^YzG63!3`(A{L-5E>J2wCoIh|x3~ci^!DJkY_A4hENStYS!%%D*6BFQ##ifPx8DP)<5WPrkWo&(}8wQbGG+Fo!#y4cBT}R}mFX z){6%x)SEHC1flxs3S<&i>D@H`Csa@RHXWu-Q^)XgJ7SlDlKCp}9SiWv+o zryA=id(8NR?y#YDmr|)emIJ0PF@%V`f|3i|SX-@L?F7uwYh~?D0-E?26gI|5&)&q$|)O-XwUb)d}@fP_|sL<+vdja^WpaolRtCbQk zoTbQY)3%z*5;V?4`ARfSp=RXiiWjZ~F^VkVbv2u;92XHmR^dxyRf#L~qU}*KBIlHS3aeL;xZ3sMM@CN@A2fgp#n{qlte_6B>T=t3UsSjQvBu8mFhPI~ z7^=OFk^WVe({B2auPkPS!`# z1pF~+mZ?{SGzS;?kj$YG~~Tjm~y;~y{aq^`9YYTi=OaWzmE#6hufgl7d>HWa6+ zVOAn}%X)o;Ggf^Wp0x^tc4BF6sfxx1b`m&G0wnlL18G-OtTvIKX}*?Dbi;4bG`5~=}E;!MRB=mFuwyE07BEaMhy!MHl(;U|QZ zFSO2Q`fnOpoGVR)90YGrA6_4EI=HdNFysr}E%|q1`_!C7T9%%S_GT!IX6-z*{ePbwq) znLj0R$)RJT5Z2krqz?J+dvrAmo}8f_zsY>#y`AxFf|j3Qh=DysK;=wG1u7r@9oGQy z7{o%z<&y0RI!=Ts=okz(Yr4gF{=r*|i3KmK=6Zg#CYn@-Nl@?S&|6XktNo@*(xdmXeUTDaDz!u5JNN*nFw_D6-& zsczBWKsx1{f3S-QL`un@fLAk^O>*{*8Lu@OgnlO`W$=e@CEO|mb>j+9kNHvtXns-7 zLivR=8&>2_&5TVX&;=9Ab3I;X7565`eFCg?U##68dn1D-l+ZDXU6J3v@sJKaQqf}6 zBt|j!UD^sgmToS_kfYmjR$aiA?>5TZ_(~ zpF)?~2M9Q7r3`EDknsjelY^MKP*cS0*9^qjp`>atTdzXW5WC_}Y~1ow9k_~sT|DiIy(~mJ_#}2>IOa91d92b2TiA|uW_a1eNDa2ox8tHy3JP8 zHTL%_JV#HnL#$HQ4q-l(>F|PGJXTVomrK*MkTBtXjTw`xn`7uVUm%b;PdraJ%soau z#$|A^DEOzBiqhIucz1NJSj$?uE(?}=aY!LSSPJ_ZJ0Gez78z=Z>J+H#CauT}rV7&g z@?rlI>T*%%94pR(?o-L2xMSk1*+z+-bP ztE((qO2eB){9d{-x3}_ZPV7y86NMS0NY`&+GcAy*yq*u9kS4C%Yevz)&9rGV^LSUNrKT z{s{YkrGo)Oi()gGAWYN6`K09KkEo^W({Ge=H?H)7d6rxfly3mh`)tbo#qxMa#Ppjy zw{%bzg=lg7_m?g>N>7a1!!N_hz5Extp04B`a4hAfGAnRbSJBUB#@t=V_6l3sw~ZFD ziN{|$oT&S#BG7n*igbti(I1XOQIOukByCqb8+S45XRYD(vdVt<#C6C%nh>_r5`G8n5CJD4s%XB`dF0 zggpu+{RQHS$s*nkw13qC0IIh7sqRS_pZ}fvV|q(}dV%a8}`UI{S~R`zI)H%0l;Bcq4Y6%8tqJQ8Z9F->U zQOtigh}fsUMAkI$of^QjX&-gi9;Rfrr4-Q7qkDI)puwQ$!HEdmW1NVO@!9VzOdy1v`V zhQroq?rR^5=jOE}y#vqt*6C)FnAIXghzP6m%%i%3nnq$}D(2E{H*~}QB3X=-8h3rd znv?bdF_YSVXOaeqWY_L_gvQ~f&}^d;sJ%%L^@X#Oh+Yvy^NS~Y*wI)0G=@rvul*fr z->1Zt(@7R|n->=+iTwl39;%rJ2k%v-DoD`|0_=GetV4j29z1T0uDdHXA1i-8^Aq+f z1Q&>de>;g!m#ReXKWpznE!O2G@V_Mt+9&a2d^@t$?0UumeK-;3%9aGoRirERQ~%jp zDkaYpu8dfyUbR~2(A|@?#O-0~6jaV#`Xvo9Z}X3*#>24eO(9}4 z$<$`s9Jf0dyZGH4__lTZz7>%M^<|TRK0X8EKXe`bQrX`MT>|^%NxUEXd;E?{%Wv3Q+u1%8FNV{Cu+XDKj_c-5mziiWjN&&}`> zC`4n9AYC(#?HZIak2Pw*9qJ9f=h{jj^~%cckgefV1u!Ne_C@@c zD31NS^PRp?@2wW`a?I})WVTOny(lgo)5S3{Oppz)Q&`V{VZ1XFrG9NXU9|XgXZ4h> za@z)e#B88S6^7Aey)F9+9@CGTQ_cf2NbzFT%`zkW% zI+F65Gn90j1>gu5&f;sZ{+iC5L0lXCrAu0NCn^k zSbJz&py?vL`YZ<+L0NOVW<}K9sH4@Wx95^d(+8OXJQ%8j$b*O(E@=Qyi(PsoVl0>q zzpSBf51zhXo)%sEQ)IPEQSJMJ`jC)!oa1h^FFy^oXy|%46%uz}BqCnnQkA+I=+GcZ ze&NIcub$KLH!K&py8(w8A`;PYX(;%2wgmx0qhFpRT9IYTJb0>%`tCzT;zxGfwSg*( zvQ=M^#ip|0ug8ao!V~@VlP4mvn{4@A$yDBV(wquJ*x1-1HakGKlODdU(d5_VohtWJ zr-4sMH0ij8B5I~p6rDDKROa}Il93fZ9S3qmbc@N+BP9Xd`6C^JC$BecA42&W4bu(P!*28gA+qUgHfS==_v>el0Y_6g`y}71abF%QBudFlQ~|Z>Vq&a za>%?H7EJdR+8HthCj*$-3%=rQ4JyEXX9+*D-2@2NgKs6!uk5Mqej<)__{&Nd5aPOq z6_uD0#=szol1~+cv%aSOUarKhWh$TrNgQp>MG%9F`w*lN)=32*y;IwBoC|~d|5|aiJm{F5n)K9$Si6vG z3~5$$HNUJ$|8S|qcHW7a7Nw|#)w$`8%Z-8f{zQUZ@UL#mVMyviRCQzlkp!XvqX7Uo zdXUO9?>4VrM^rZ3gSXR8fMc*mNlG3zzyNsbQ3JAiRT z!kzB`W90n8-bx;Xt!}rsX%i2>1yJG#VG-|LxT8p`0zkUHf%Ix)sc<;*8e32Z;1zPd zbumG5|7tllnNBqJ{eY}{E7f`u@b1Oyio^g@q@aSyN2F7ks!x*I16y{&K@|WNF{(1r6mwYGUikCh=E$gUcA#nKo20r{vO zKBLAwD(OL#6DZ??O5&hg;U(~ziTMK)ynLjfuYin5`{!oX0FW?~)a3&V1)#E^rcXBW zp&v3#<;&eb zpU1~qtAmK#0~K%P@I?&wk_btG5`aLXEX1LFYN~F=WsYl+CR&YqPCg!WxD8P~B%jbt z_#O(19!c~mz@pKZ-IMCm(Z#EmK9`fCO<~Hct&P*3D@wMSwKEM=OjDj3O=R&N7)d+} z5J+Q69?geZuCG(MEFcgHn~&hB{S!NM?4j^6?`PIClJlAKfb58UvYGNQtC!3dmaP|S zUW{9gMu>0yG*!nCg4)s!Q$7Tqba@Gw)Mduv)qsE_6fMX%eP@u%zCc9>hFG;V+9=h7 zBzfYluc-i~t)+Lp(wGCbE+91!2yxb%r`uM0h?}up!WEE?7D;;U`*?n1$lkElQg`v+W32 z?d4D6H!1ZdBKA5Fi|s*kSAagCaegT9+;mV4LQn8i=GeX=Wu^i%8>a@Nd_u|?_d|iFymzF@Ta)m(0BaAS6kz5+ z@>VTf=3+A961_sH#D+WN%Chs1TKcqi=u@H*lCN+lyDV`)A3!F!MB{F}DRdfZ^MjY5 zevg3C5zUGuuuJTaj3;72GZZC?C3Uw4r3{1}Qqq-7p^TXjHnYcwQwae&QTI17=lVUw!8$^Dp#a6b_^#temLq&|BXDwpr8Kd`YY%fyYe_0}s^h7@NoSR#@PcJy_l z){v$t7dhf_EDGCI>YKcI1D0SD_8v4mBr3GDla(@L-rm)bC+qt|pPw@cornj^6p^pz zJYj78$U*z@bU^Sa&Ytweve&68msu;ps|1y)st4}9-^YQc*)2pJkS4bsIG)}^BdB!Z zcZ8VTZg)R(PF=@6URGjIko4A2I+i4b^V;%#Vzb{@ zr!xf=!+Q7}O5&3r5{rt{YTHZn>vxtRy36EDHE#EAq|ZO%{5Hf>+UHp2tq;^4&1k~Y z%Y;R1M-*P1}|jMMGSZ*Tu}ZZQWznuI(ETjZ;%3YV>3ZrzMGJqAKgDw%2V`y*pXQLaMQCiERiIH zM*#NN^u9Y1e4h@ECm>*JbWAR{UoUT;W|S3L~QF6n_dv+<@I~SPLpXvdhU%BAHHM3?9UX# zfJ7aIjMDOs%e10@B1>~5T<=F|}PpQk@eb%86?3kef_(+&4$r)TbQz(s0ve&O|{ zev=HTlF0X~<=+;xDqw8lmu5j5Y2U39zKC&rncjJ{TOHl0Y)d#)%pdqJLE(|r#T%u2NE zEnJnms(5S2P0F2Z!pvyA6nKfto}Gi3F(g_s#C#}2W?}E5@{t`z z)n@BtFq=E}<5BO=yC!v*mHha+Yve_N3k{t?Uofv{*ma zww0aia-3(urRzkN^pi*y_99nMdH>-<*iG{*w&Qsmn@4akup(V1HGyObI^}g+>tNp& z2n^zeUJq>Sw`(<$q9hy~1YRuysc?h_D`I}nBd1YO3l03}GH z`WGiW(%oLRIAOzj`%UMmi!AQzNj%wFr%YFgnC30%xKYSa}0;5iZ~!{l)q|?6*V_=`p^Yo z{cK}mC=aXsW&be&p?suGnFx{2i%y8WihPOto=8-`0v)#^8T`LOciy@H>e0J?7V`sc zshFAHrcA~k1cU=%Lxvx(HX-BA>vK)MBqmHF z`mL3ojSVy5m-io{@5QS7aTBpSGWR=klcmu#0_q)~hcs3;9N;*HN8KB9_qBe{C%$@I z>~rE;jQ?sNF;3k+3)zs8HFiio4bohWmOT)J5}R=GU{Z|wy>(NLo_^&peWGq z_#Lb}4F1vSjq8gK7Af64dVd#r9w8VmcHRCQJtJdv$-@!W{uB8K);i?m%9<0?ONy|~ zk*IapB+6bCe8wERtNXR{;wkET)qvH)qdPWIxqdq>pPg!GU|w5K#4)Su{)ogZ{1K<} z^8&?iP7W#UA1Ut`(~_5kN`rZ8g3z3POG1gK44^Ee?ydaf7t3(?N6zDzgy&GIQ&5Tj z?VhNCvp8;N#m$-0$sHwceze9~6#=zn#gpOJD-84Yv%y85fHNu-!ZI1cHe2V2lkL`w zO<2X)c<5?*TEwT&|6;Cx6&M&Z?CrIIqF`u6X=awEHr5ak;3ghDg}AGlBl?#;Gb@1$ zD~!FI#c%t>1n>^I>n<+OCQrG}+q9BfFAw}o$+|&N-w#3Ve>nuC-zgE>8TRYJrrt{L z4$K79{`JJby4$-gWAc9YPD9Av>Z@^q^Z4CftLZ$Keya=?&2w}ikYxv(rKI$T&Zfil zc%L8FmddLUBT~5!htY;L8)9|1h_l|oYA0x~pTE*}%Y9-llc7o)Jno&1Tj-Meq_}!+ zJK2rDErVTxIwm%5h|RSkSZ|l6@|IJ8_x|*eX+-ZZG0W?*{~ISx5@SmO_!mcQ66A2j z@GLxZRpr087L@ZD3TY$&-CQ|U5ixB$0pU|N?sro$T%k!7L6stG8*@r04Pac?w0bIB z56#uMU3r=xtLyEzT}slB1IjrddHcB>4P68#b}W4IMJS7V`kkF!1%d4d!C1Hl3;Q26 zKB@r@M1t06EkEyVromd+8v7}Ku-ux+cJ0L7KlK3bh<1Jd9=RwY?d~i0N+4#?2}HA4 zdA&GpcjtJJO{+i*s{bOGj?*5`@mGDdY7WBN+`x9uIaeG%`t16|kd`;TH&x5hrg%=v zbm=bfWJHJnkDauZmMbRgvxeIIr1v$Y0Dwe}1}DQ7t8=L+ z!b8jYU{gpI!Ts3s%P5ZaCuOMbT<@(t%~aqm@sW`z=G^k0@fXgS9p}jVE*~S|C*j$L zeYFaWYNJ3(jh8-FJ7EcXv(U`(-9c%HcP`QQz0sA%_1p<{#F-9}j7;8IMmsO{9E_UN zcII5j1NJAd*LCmqZQJ(_z;Nn?siNvT280BHT;91OEecm)Bh-~E><(r0>?p+JYt@>c zWeT62+iUCZW(b6c`c0?*byx9_KS)Esb&50mw*7DP8e*Xiviw5Cv?&Sng45J0)68=q zr*vx1_yx*__)aBx7*!Q92Qe*FV5U;s&`>|i8}6^EpG|51qAdsmBqi|q+pJXUYp=WD zjHEoiD?E%Jf7FIm{)*P0SsxHC#8LDZ-H|X@3d_VRuRDRPv&c4;x9rO1HCNVD?;Bc> z4yfvMWDu*Y* zN2Oo&??!HY!8(}dO8iA4MtxUs3m1nZ{EB_*zJ!UbuXLc7#uc*XU`kC9b%Zo@yCVDq zPLX-X!{7sF=Be84o72D>BAM>IBo=+_V$MnGj(dqDex(5svK7QUbi)V z7zq2pE(~FPC=Wb}xc%x_Zg>lY&;B*s5T!^jSbpole285-aI~*Jdtcs)#^@d4J>3Ai z!48l}76dD}WZ4AI*r1bF{*3(cX_wLFPR}4f;F*+9_`7~Fj^v*^KA!V1_{W1nem|P2 zk(h}IfAvR4u|GLE&o(YnnLrk4Fk0J@3~IW}z(k@48b+0AkWi$T-Y$FHM2@&Sos-{K zmls*=X7L1`xX&H+1&fzV2{%r!`@F%ec}FDwuFLwagN3EhQv{{>2j=;AqWJNOp>-wa zy?*K@>RC-BHecT9y+>(q(m^ZNWggrFl=UPTFH>a5Cc}Ha z{h|B(wZwV~uV)!7_nQa7fadS8u6+8f;r)>QmE|N(KSl4cmD0;y8)NM$3)_KKs&W`JC-m3N(U%S#nuB($-X;Qb$#hp5+gkPU~+V}Ct$ z5z>dSO_xhbseMDJYf1m9j$S=jfvba`P9N%t#-6Z0I}4smuz3!6&f!3==yn_4Kxdff zTPO`Vloa|@y{wcR6EOMBM#DP+TygRs4trfT8?b7J{{D?x^#yEr zid4UYJw`@?{qf*beA}G_Md|rqJ+ISL!!dzSw7aP)NV3E0wEqUB#%kTAOuro$VP}M{ zMm!%#V=|A45w@OwN&D+1ZQk}6b|z=S=Q^UuCS17@(sA&TJ>U8EcwB9ufJ}}7iT2Yc z?y0uNrVNn)k{1?%VWO!o8O?$lPnV^T@+9rge9}5Z>U}6xNyqRKsZ7XH`>t>@7+<{U z$P*6;%`RzO8?n;d8aocTZntByC&n1fo|M_2;7Js0u5m%G`+unV&S<#bplzZBL6qoh zbb^Ga(KdQSCweEkDAC)B=ymnZMvrLGMejuKy%W8yy36AI{h#+d?>X-`<-?x6XXd(R zuDNDzD;228-I!!kEys)RnRihYX?!K5cAvOosPG@O!^g9yrpRE`X&qCimm=zrynFH_ zJb?UqIl%zX#2T7F0;S;`5>QieegW==bofWa!OkY49_Rp;t(~_D6y^F*Yu-)Fzt4|V zx_d2i5918BgRx!HCC}L_{;w9mXROcsw8F-?Jz?g7ThH}5cax7r=k#lvncr`7BmxLh z8807^^nCpd4G6eX9{=U*^cFT1`@Ygh5Z0g(&#}-qC%Dc=hlQlivKl|)}C1SEmAv$MYCPruq zqjqIaUxPW?PZtB|@8!9=(*yeQR20RLCBYF>LZySMYz3}g@YgU%e}8F!=Aj&nmsM?d zkC%uB)PB;fuIN9NFD_t(g$bRmGPw_K5Bd+y(szA-aRWEy|+v@lishi$M#FI&NEZxAKTvNyggyp zWCX`L&=sZ0>AgxmeAXXPGv~59{pquZ0zOcvJPQ%|@&2JqxXPlS*E&+?B&v99b-n6T z>0cAc*-Lp>I%N9H$~M(2ykt89>3(u8UVBEPy8>NlpjHt)s80_zU`v-%*3tN9AH&z& zzkce!>vB0Qz?I#(UC;rI>Ho3+*d{p?8qJ20CE-b0XW+fw1{FG}J%4|1GRRrrxp~Pg za-Po)K3;DQ5KAc+{k}nOFX<}d?|pSE{+cf9n}eRz6UVRI(U*qMkpP%_*$mLo4qJZr z*6b!jGELBvEjGI?mSrk^C<9&4t^IMah&sa~J#wkIi26V!y;7ip_By=kJwBv(j^=kmpFah7tW_Ov&@m;*3U6J* zTSJ`ep&g}%u7Lh$XUCBz7|LdLT7;+UI z`$qXO!>g~%H!sVZY@8BBs0PN`m{NaRUEn(C&WkE(_S6}~TFm#j3pd*G{3n`_EhRLa z_!TtYy5p&odDoy2aM?a}1V4m@t@qBn``EtPb{9U4Tv}h%%V_0=>RlFs|G(1W`MHHP zshvePF9Xk!?f*%ZgYHc{LX%o|(ry9HA{bn-HB0W$>Qz2U!k^)7jV@_-CPm~9*f0Xq@LsfuGrUAVnk*_*vfZXL*b-ea_wx`cKzJl zKBYI+q;Yer|2@iekMSA=*MzLK`n=RA{8&*-a~UB4euYe5;3mrvI(-OZd~uhj6yYb_ z;{BALkdWl^y|ROY*rNo+=?}vzaFv6c4nN|bBA2o2Q{XP_>J1{5N+LfsdEqzbAQBpH zOylBpVJa-jCf!Y#s9vU~^6XRxLk`f3nl!Z^BcqOCMG05 z@juQ58k8CO!xV_oeOFBe0nL7%CX)|Orfe@B`bJxKtGpFVTKtgY1fnXvnGL5dZcArs z_Y<7pi&havd0S7etYut*wm0RqVAE;5Wr_Vl%>M8LA0f!->9AaPlq8T@dCPw+|F44+ z2B?jzx)thj82~`3llFt8^bsH#K6$nrZjCG`JJWfPkG_jc|8cX?$zll7z_V@p8MNG9 z@z&7S--_vE)Gp7#bTY*rZP$<`*&cL~0|>J0e<&o7{RuFNnPve7y8}G0UP_3Wl6e?; z2i%bv)22l|OBU^Vr)t~1mhDO=KluH{Rv3!k*CtU4GZ`%eHM}2t3l5;#3A`O7kmq$m zLb6(&ao(7;OW)RVs6RxFjpwG**xl>)Z6fv$c_EABWlJ6;LXMBcGAFN&G6<9pAOtt{ z;Fsw&`5W(gciF5J`F|Oqb6PbUl$|!1DQ@su(Cnz51kc8_#oz*qK6bgeHoAAa==k?| zsOkc^f&j45V*JSsTX?aS#ED5loSnWh{{p zjwl^|&!L~sOPjukP1Gw`ejdwMEdcl3FE!rt`w$wJU6%Uu38`_5HMz5&?|hAj_r4M3 z-tG@=J1HprPpGU0te8nuvto3)2C|tFOSDdZJ;Ls>suI-i_znsp7k4#c<}o8YB+ijg zdU)p6^4Y_v9vSU1PQTkil067y)LEBX7|;Jjd;9JSMS|tS@HUlkyFeAS1hV~opxN$v zQu>Z^k!e0@?>R0TyP40rjz4w2OzYh1eD1-fq(z|5Js@b&AB}YA zKjb_r`d#;{mrfX!&3ueF-%0y;*6yMEr}r-l7%%!VBuMj|N3`pD4SC#~i7&bya6$Rq z_%Te?DTh75dUE>phYUe{jqOL05{XCRl1M)s9Qj?IL*IZooVl>zG#Qy^A-_*w@oYZH za(%0sUopPDbyZR~IA;5CPA=z!#ypRzq+1TKmIV{3W$_9>1Gq4N5J>iOhIlsTwITCy zg2I$Sa{e(L#SLoCmzEt2xD|w41ub_MkC1_F(QOS+3gM!m*eBg%A@4YKaLD_Li7M55 zF3f)3kuW1|zmBo`L(IE88CgkGK7ytEKIZ|~cW*PpAQTJsV{g{!&DdS`in&)q__;-~ zNdht5>{3+oi$o0nzY})kZ$Ir*PV^_=<$$uFI2Vi5Ie(XYaWn#qSM2CI13_@VM+dP5 zF+9o2boSRMsl%s3?@b=jtO+$VgF%9FE?_7 zeRWMd?67-?QM%A$oLM<^*VnqkeUB=!JMTxs*!pe`?tYtXh~Y6s@H<2^U*_fv{2#l> z@wlX*Q>?jI2LIOi%_YYV))LZ$0uOZ+f|4h;K~JQ# z;>Db(w)d+ROjRBIFuwGkdd=b2Oa+deFe18X{?UhR+is*WSf}0Wx~zTstY=7Y1C$I8 z8NYW$0k^t1VntN}*eDQ_1QQD#?GFKlx0E7|>bs@)!xUp61Ns5AxZ&ukr|TcrS_X9M zXsU&X{;rwf+WH4KNRghV$v_l;Zx{ciQ-`GW$R=Uy;Rsd}qDCG0T6|=X3Wzn&s#R%{ zn9_C%?1*QIw~=4sYF1jF*4>$^Q;4mV)=cc!(kZ`8DEjLF*Govu+*)~lc^<#fWYH%! zNMVto(f(4eUW#?h-)k>SNFwvhZH^4H^%-x7^Kcps^6oS!QL*jm)dZqQRP$i{O3$bV zTKkWN!87$m{4ePN)q?fv=FAY({7TZ|^f$na8FGWFs5sa<^r3NCh=@OfnVo+_n_Y8P z_2G?6iT$lJQLiwrr!{|HDJiG=^c_n=!3X=dZ*Vn{f0*zh*qJ>_;^s^7Q`V>$z=BsH z6cpM1Z2^SzTzfz4Kjuq51meV3cL3 z7*M8{oaf`?k8HMyRc`DyeuB)i`@6?6bKu2Z);-NNIn8o*R*IF{stCb_CqGn-V+TlW z;5JCDq`v&Cnz&T|`*Q*G`}=6d=d`rXaCQu;J2yQ&nsev!Q1k1UeR8SEKd>w~yB`lE z3w?n5ZCzx$HX)!>L!H)G-mhfa#8g3V&tUtoea!B6uzcz5*8X)|ftMalbdcf*B%V6b zn=bwEnQY3UG^18n9PC3Ui%=#|Gs&mO_%S}uK>eIpCE-DOH}=qtJ*0=WwANFdBhoB> zLa+Ni7cWqU5zb{g|Al@4G5RyN?Ag6BZj+6DbGttJ7uWf3S}4QEai<sbvLj7~!Id=CmbvXFM}8H+-2HBY&J#xi1vR1C+Riyvdkdn*Pp4n1 z`p2$^JGy(@U(M~6Zi%31vua+{d!4d9>NGC1rFPDRPAr{Y5t^+NhXfaY2jDR1MhMCY z9S4{E?l%%ED>d@g&f#xAENtuoq4- z(9i!t5I6ii7x=-Ku7TALx(;>f+{?H{z3&rd`*1#mu&VVF$J3$>FBN(S+5O$Ow*U`Q zYvXIaF_96#m*J89$_6y|Fxu5Yd{;he{{gE0G!yQi%84nAN_Qf;MRJ-ND(IJY4lKt!0`5(-e^a$l|G$!@tg5AHva8yPfn zM2N@ur@We_it1*oFuUyVq1>b2)GUU2c5#s2q7OYgF=~= z!<)cK><(Onqa`mu^!=9%3*E?@A9JeCS2t4Fpxog)Y|b6~*}*BJM7WKfYPV%*r{LH# zdiAt)nG;xtMg8t8NsBqN%cOEZk<3l~ph1Yd_}nG>h<5buC@g^R7Emc!9U0EjATOFIZ*{6> zDdTwJO^qdl@%~g>^K||jcS%NZ9Y=uL@5BZ#K#EmDbmxyLfEb!K=_uTQ>uX0=AJrezj zMMLhUd2qKo&f@lH@$U5rh}gdIHv7dnDQwLIx9Q8pGT3%V;Qfh-PWq)DtTr~lZBYL? zRwyhi$l`73R+B&p_NwuGuA!>>I*s4;U9OFpv%4CvpP!dP2~+hS51`b?zm<9X4X`Ke zn42%qfTjzMw8^rLpB3U=E(iEF7OTzc8`}~a`x+Md*eOSV)p!O!O91xBN*UJRhCgls ziocEcU)^~1onc5>^pTDZFPq4pd!k;{K;w~wGwuwvYFBjC$iyGZ7m4L(u0A$_2hc325MhpUAX4s8m^cM971-MrHt zISBsr$t!v=l+JqM=LHv;$4KOq=!48;fkIO03wvltqNYYK?a=XIm#(P1^-A6wk$={- z^@*s)knP8)bE)uNocpx^nd`~+jm3=#yor|_RFCo)sQGD9^iBpmax%hnUTzlTaKh?ritx>ddp6xicMwVxJ^Pd0MDu3u;R8X zA~AcW_G+#|xaLCDVaZie4}2lM#6G8z-5jG|Uc9-nFm6@Ji<={HUZp6(%WO*nHXS&P7dq|}qaNzccZci3UMgniPDmYYoEv*x zH>7IFCw!t(|2&xZl+6n89qv6+VDm79)8aoTiiwC3kjA5FVE5}owH!G<_&%i576l0ax7oiDz2k2ya) z)+8Wc_dzjwK=qfJ@LuKNfGV}6{rQ3p#tYJ4_F?jjU#K&B!*YrlNQKN01tYo=txVK& z9L3X$=ZOsdA5pr6luZw9&qPzMWZW|vO2$AUE-#V~qWiB4#n1AWcGWW;8!zMSX0P7# zV#aS5PAh=_INkx_<&X$<_%ai;tVX6(jO_mX^<^Tms9-`a876>EdK%Ax#&;CjQhi!@3+O zBPYH~On|wz*(TludSP`Wq5PcY!y7BvP4{#RIl*we541v#A0d>jug^zM_{dLsD=|_x z5VL{oCL&;JkBkT-9Msv~5vH)HD3u^Ygr%R9uTM}j88k(J+J4tO;d=J*$Z!<{{ONVY zD=@ETJEZ#&0lr0D$`+Ha{ThUlAK!6xE&mI1K0ae_+;NQ{BP6D4Z}ZQtc0~iiGL9o? z)O(H6^ZrU+f;3LRRRm~}K*!o?LQjs{y0?jy%Nz^#dtj|@VEzgt!Xay(s%CN^DCG}6 z{r&Ck;+b^sLbiGv5PX`$;QZCx+?|^+yU|ew5-Dr9tj;i~u4mGB?6BgGzSZLS4fEf* zWC9++&&lX4B^V6itg=&3)@~m1)zoWE-}&C>`9sF|6UQ|&_34^f@!hS?bhy_oiVoti zQ}|PQXY1vvsMrk=Qz?r=4mmXC zW|HG|iIJLfEwEB&h5iBl_998=G4jz^Ut<-dpJ4$X$AdciK-n3&!$+G>p2-K z3TSotMQ;-x0-RI)3^D9!Fe|ue6;MWJ>ftIw7}p%r512L{CD0%^jZ|3mMF;j~Jt9EihOstH4>Zc`EYzSGgYMZ2OTbR+Me)we)~a@X=&<%LkVxInV0DaxR~Dw9 z*US>@P5<>JMt;<;YT;ys+5{(YB$y#XNq;cPnG_!+M|ECQt_at-KF?`=i3A9`+zE#n z3yEt`Yb4ieTb^G5bRp9=xgj?{i)_Yi$?bIBM2s|#Zr$I#=Yp-$uO)eg%$YR1Y)=#T zAt;}M*6s#UShxkb_kK+i8vx5W92o4auc&t)CWJ+#6MH6}S8-d1^?rD)JCUW-<`v52 z`!mvqar<*i)eMnxI91(|y9} zP6+4vuL1rnJq{+P!{pDSY7#yPj_%nDbm#ePKoCUg75nyHJ@13ck^aJz^Y02KKI1gu zMhSfZ)Zp1o|6^O%mvJ=%zvqLMxJZwpmqpueQG(s(D)}v2FFcu zg#-yuV5qCyF$!s-LUO`D$x{o9f;GEFS_;@kr>6hFhy)mnp_mil zj=me>r=BOE?jcFP{~Ecr7Xq2UNfWVaO`OJ8o>n}KIW1qKU#2#hadzsg(uwC%43Cft ze#P6of8To1DA8E3QHcxylQ~Lh-;V%v$#=x#f+*2h(J!Isb`Hza!Xxp|rwoh%@8elC z1;2k|uc=Ob`*}d_{d;tD$xsJ8_5th#1FQ5W)nN`c`-6|(PAZekMha5}wzt{48xw-N zIliN(%d5*b5XD$xIMduW(dWNwQGTW$rw(fjd!U@x{gdae9FzQ`@rAr6$SbF`I%gpj z2PE~obj)v+tL`G)9@d@b7>yo#qT7RJi2LhS3Vt}L`o;OA?RT46)_%=gyvo@U1Y{wzcDvZ9e{i4;{-Lv* zfk1TsUcV*#OS+Yf(u-&C$BVk?uYFYA($BZ|g}~&pS|p>89DKU2*%{((<+GK$LY(}> zo?j1z*p0a43uvP=!VjxF?~Br4owcZ=-fq6cPxV5jX`n0QO5#>s8jN%9B;Ry~7}4VX zB1nrN!F{Fag7O`wUkoAlYVR9OFkcUfKgYq@*TC*+*KUh7*L*}}XnD^Li+H?<*4uI@ zE5weh&i>J2B*`Mb5gPNI$6J2?QQuR%1m9c zzU66`y6>dQ1s-iy^%)+RD;DU8(rmXsRM2dr`g)^l2|S9mdMRbHN=v|PJ+~HDp`ly- zBopWDA!573eEBwANI=@6Ug&f>-GTaJ4^%m=K$96$1~pJfoQl~M1?qP2lR-6pHUNHH zQf+oSCiXkHz?Zvzq|_P=^SnP1H&50M+kpk;*Buou-Ao=I<52b}=zeJ)7~Ly-&>w~> zpm->=>&#GR>W82+y^BFz;g~K4KT-b&@(c(sMZ4b`{i)6?G~YQV+6mJI-bj#@Z;eCW z7{O+L+(AXSa*a*1>gMc)5@<$gsLV>D zopTP9=dqOTb$%r9`iMTZ;canX>!DEWocs9OVZCZ1B30G@z_{Cm2Lie25$4_r>;E$E zfgK|iakF=qv{wePCt=9P~RrqRU`awT&BvLM~4&R=N`n)(5 zLehW9_|#GDE)R6Bh^9-7rz`HEj*r`}aC=3o|J4En;s4}&hsa*FXnGg^uiyJd0ZO%C z*2$BPn*%$W@#6U+xCKF2aTXm<#(AFYtjZprxt-R@kB&w;&<}3Zg(Irc>)1TV;9C*R z)B&@fZIQV68aH~x4V!G+SA1fNA}_{b%ifP~WsZkoGAQi$ebD*C{dR`t=^LjaXKeZ} z*;a#;gRpd&eo-Us0(RXO=@KYBj`J`}(XHLxref*aUvyoqJk6~IY&l2wSV-)XXP(h~ zo>KtMu3D*Pb3Tg66b+)F-y#1zU#xP;CB{CSFlj0*bPlbri+7ezPoz)cB)z z7a%K{+H#S4YBqDq7XM0C!$E(UAbD^{R%zDhGWbAbTWDCXg@|ZO>3z{@{suG zz#`tAUF?4${;%-M?=l%HQ@{{X=*M8(>{{r3GOZnN$31gjEdfUWZhyD=G z8(ybs24o0wH#YhZZ%n9NlM#~T9dOxLMfc(v^ycCF623@!%}_n#P$lHpoMf)%Y7_2!H_Q zgC~ZQ&@K62*Hl@XIIuDDxGRVtKd$~O&Gu2txD@L5YNk&0r*Rw{#G4V#s%1U~v z+1WKF&2r5ADQU zHuk~BdS}HUjlSkwu{lkF`sLP}#V*_v$(w8$MJyk}B_9VuzbyRbN_m{e)sLU5Hlg7H zZ*d#VVm$fIO8IP>P4RfW_bOc({Gd- z>hft=2<>W#gnm^1LQ7+FBr?KLXZhm7O%`YUD7F&7v{AX6_VxAW1qxs0K^Hp%jUFmY zax3>LkCq}TTwq?QwBLkZKk)=(W35ZI8$R`ht6}tKh?U*sUs!GMEM=znI~oeKw6&;; zvb*fGu!}vsbu*4|pzoRZFTAMg#rfrli{g zu^MYUv%;R8db%Oofa`Vi-HdGi!t3{M+gy#xmLpD6jCLnn=U*|OPv(A^!b^K=T}%Sr z2L`e9{P-YD#z-7Sr_pG{aX;Ze7DKYtG!>5zT}p*$D02j9u66&T+50&n9`8}Y#D8q8 zTgL5Il*hjJzakFe)Wk&^D!wy_+rL}+7jw7a30z7;P8o1yFo5J0M!p6Oz)PCap+r6} z?~3`3yUtC!ZI(@}+2#6DTDuKDBi1N9Vm9{lWl-}WOIspQ2zVoRj1GQvE$S1&&~^RW zvVodXZt#PU%I08$Wqi%y*MidNw;yxl8iefO-9eX~l8wUbpoHDh;AL^`FjDZFH%EO} z9CdK*?hUWfP9c|SC%`ZHUJq$lHaI14`h_o#q$=)^Zd_54kbE~xmNQ-Lnf+-{LkYQc zf8#@F6hZ@#3CuE35>RY!o4dbS^#?&^vSZO(qS=wVCyI0Qy+ruerZ=xZ2pr7(&*d(< z0=uq9V4oKh6%rj;087j7IF`#PaMP39YPW@qLUTK4AiH2|kX_{eoAq$B?YVfinWfAH zcKM1QW5VYCiO0soA(uhl=U^`NCX*Zn$dv{btwcyz@SO$)*~q9b$OqFi5*~dBBH{30{?~^aM}uaQjNTpAFbZ(hyQXLsOmi68 zmg_4a6us)0>Ns|BsrmVnd*_Qic85i8LYju`-o6l{+A-rcpP*;GI9oG?fA_{caR*a} zLnKUi3MKe+6W+>_DBXLfvO;b>0im-C8>*3R>DstsJ2ytX;0b{MS-sq zv=UBd6cr9?j`y8LXM~8^iEgc!bM>f3+$dqA!?Z#_wK`PPqt3FNPK>(Bk#z9eLG%%S z7TfwfQ{JOXtwkp9oa&-KvTP7~2NdDW9HA`STXLM`e71*ZOPY#^>~fnN?-@99+k3Eg zp$*6!c8YsnZe^TvbuFNe;=~$D)4)tz?$@7^_9um4uW#l!{T{v%mQ%i)VoW($zz@h; zdxr_Tc_iSbPLd4UN#;TT#TY3?#j1QUd9nrn>qKoOq&G!Mg?MNf)FkheLV)MDD88WR z${9vGpV^zW-g8pxh6#kLxja@7@?#JT+;LTMwGnWyD~x9RSEF0>3;75%qofMSI?+`4 zN*(7ebOoWuG67$)dcjADZqCxVXVec&1(~M>W6Pvh*R0o8+61Pdrm@*Y_V}2f#-> z8r!9meJ%GF%4&L=QK`mm zVg*nir0o?F->LF5&HnX~A{_fXP<2ggQqqCvAl1%eSxxd92`nr_?_|7rxdXg@{$bkgJRYu*j0-4C(%s($EDG~7!iwH*?Yj*SIp z_CUR}VHCUd8@EX!0JN&Ltb8sQYT#=@xFE^4gQYqgpBy%1NuI{H>Br=amUCLn=J^Zn zEzx*?wdw$gj#>;L)&v;3E;py-_~m9Ik+p4WO3T5pR63dJf(W=)@eS_}du64ZnxVMk zFB+UZpO={e*4{T%>TJQM=FWiI%4q5wQSU_8JV%xYfL~8=1}zh2@cZvm zbx5JeDrw0tYMP>LjVof2e`qGf1spWDGMEJ=X zc04<3x`)(Fw-uO&96zkksb|CP4&~N6`zB1we+ZXfOdSoyO%p>}k<6$+(!_2WZeL!A)(%AP!;R$?$UNM~c$ zGHiS=fAcJRj&~B0>m#o1ARG}=8oSZ6VBq!e7R|}}W6ay$ntb~uptoS9J|z+q^gUj9 z?L9y)Q4a+;I8{MrBT5#|*$h#;=pL1Ov6H>7GK2lXDsD74LrL0u<9UqH_wFKe*X73% zul}6}8;qq717jHzC5e|LZu^S@tR_hQ$xxO{dS&95n z?5Vqirto%0E~7do{?Eg6=8y5(-Bx5^)=&{TlEUF{Nm!+fnyr#Wn?-$NOeA1bbc8GA z<5so^-+6%Rb7KWfpb7@=hng)Fe`i6lGa{)FjdyTM13V)eSq7(NpJ>@q=*CG9g)ZL> zAAgCF*s>VAOmzxPMD)Jq=IlywqraQ*>R{R{!*-!S2Af$+VxTgkm&I?3VRirbQZtmS z(c=PF`yPVl2@;d9R5`U+9Ow&`k;!;T6P`k#Xn`D7Ro=qF4q4wKyr_DdcX!Y`57`lX zF@w?Wl6B7Fx_P1ff@!50;Awt1w+$CvR9ozuFE}4TCG zs(F}+EbZSqOi`{|V1%Q1l>MW`>}-{nojtS4=J6+n%s9+YQGD_C8hHRB5=0BLfTgGC zP{x;={rUBLtK5Bnb53lFY}dSU>{V^Spz6jo(1*l|&bV_5Gh;?v5WXd)HmiD^b3}zb znd!^7N`5kbZIJ4Gq5B*4R08yyB$M1-u61n3Y*Gl6A24^YaeGDg2VHabKBUd$Q+Giw zs9SN@@~bSK7iqAzc#7%$tg9~4LX_W04X;y@A>$>VE<|Fc4}T^sPkGEz_imTUA#IJ= z-L8d{J2kv7;#EUXRlrme%WsbhDilvOub+rkoiMcX{DKYj20CT_DQ}a4gf_Bs>#<|| zo$q)}w{IHnuZSU|-^{5Yhpu{vY+^WRw*icn6$nNtRv;J|P9e*cE5P^d1n$0BU0;rUAit*<^NBD#g@h%Xe(v2MkRY~F_rDzmky@Y1EwNgvm8p@- z@E7NF&u$d=;=q@N>uumJ9GTB$8Uzm|qwkBK*?*H}b2L3%Rcx&FeZD$Krv1`?m_Ub=2p-B=W*Khc1V;nPJCwueT0XI|s6v94CvHFqK4l@z4RY0_g!Kv*mLjYDmZc zmIT+XdBw3RWs^ySKlly@8w|sSe*ol(I)t4wsFJF zEpgH82a*M+eaDqPFf(m9K)~lP_IRf$aQuCLd|g;BYEzuedEdM`1iuKj+FKU+I>&d> zS-yLDJpG08ojc7=>?5X0ZOLc8(4Vv96EFK7ws7WTGy5#n3U)sGX*6JxD-#&prGY%y z;TPHIh=?>8oR+2mCf}`s&+F=Dafv949Jzo(>Bfv(!bFaDwr1m z!>DnKl5IBEd)|MbB2a7Btv77zsJ@Lj7L*9j6X`p3n8(LgaHm^xvlIi)KrH1OP}i<3 zdR6h@!!^qcb?X}h%!n%gJB4KO&D}&M75C&x!`AZvAW$Q1H^0BOicU5w8~lCRuvxoi zbYw6^5OQu+pC;cv(QdJb@^AFsv;RDlfDHvyBeQrGXA5lqcs#$G(KZRhoiMX5HQT?KO^?A<9oal>>(S6@QvyB;?wnH6*9*Yi@>c;^1q zEXoLj$xnv$Y`NMF5cEX>OXDSXRCE6#M~xeSW82-D-Ecb6x6U7j2@ZbNH;xXl)I1gIZ`%c{SI9QhNej_#+8fe2f4Q_OD# zXcg#tPu5}VJgHc}G2R11nZvg+7;2R7r#{45s<#8~u_9z+I<9YcOvtoZaemP7C%= z1GwA&j(Ua>V(4NCgj1}rzz1VRytvcDr>R#SJD)OM+97f+>l=P_n0~J0C%25W>`r0H zR=%*m{Z~0(_4w+9?hgBJBHILH%{obv+33L@4R3qYM)Mq-8&~?lDUQirfBDZN?fw*N zSVIFvtm9rp*1}ZSmdnipU&}es+j|MEbLN3_4{GVR-%;uYFY$KAqVYxVY%ktkgM@w( zV&D7>>=2S-`K{cYQiA>SAB0jPmEm2 z@nrhm&1y2pqcvqKAo(KKwG^-MagU`si6*FAQ8ZtB=83S&HKE9*L4+lr{?=A3{zQqa zH>&y|EhW`-be{F6Q7ey>wA4DEIn%?kzdGr>!0HIpn-tuc^e4cqXZm>H!Gl`qwn!pl zmcwduetW1_w>>%H?P}MKEFJt(7Oja;FYGa=O>UAog2(Fp7kYA+78my4?0WFTcH>-kt~^j|gcg2G$JFBd?Od@sZ9)oOSuhDJ z4=|EC_Q2=)($l{;Q}U{B_{TqM8q}NE$sM!pxoSxTF4zKV*Ok$q2N2$a)7x7JT%zdN zzw&ACJKI#-;ZbicU6*Hf(6tU!#K`Xh=oA97DluK5{e11ZBxE1|dgHdd7|~++F3E`p z-?d*r@=kSdV|R_vV&fO-e0x3TtfNHO#+p+%e9iyIT$tT`=iLM?X_{cv!n8{^^S#&D=GCOyA{jkIgzYNuGR# zXTSZ{Ou70~FtPWRkaUts4`0)iRL;|T(BHTE@ww7NYs@=DPN>9!S%FD9tbY<{Ev z>8$*}kngENlwg083RM+w=mj2LjANl5cAMQH{a}ISXTF5Cj`j_1SGIum_K6pUDf_pT zvNyQjh&#fP8zYC$FP?SiqG6z);YL0}FyH5rKm9?9*8a2)A1$tbnA#Hcr%s;&YxNZE zK@&VR*~^x@J*e-F(IJmkg!Z9mv^S4EvrPA_#5t72rB>qj$$0ug0y^S}=`G9?eH+c0 z?mZtG!->yAC_rAMMpUBd_cfsavdNZ}U z`6k_wD!^^8r@is!EX4Mx%&AbW@a>;xf1l>*W?p@czV9da71Oy=nkn&6Fhp`B?EHel z^SZa?Uq8<$SKoYuIxbP(yZ+5Ow(&M1Su@vK`#}R0l!IGH*_sRS=;>ZirJREdGGU?0 z>sZ0T;Pww|DA-<`-1}>Rd~#6@9JSoA$f%m#9u=A$&>Mph)FBSJ0_tRqb3uH+cV6YQ zkB2z|{OWv8QktJ$4;lFC`d%WE6;Ot)aL}N?7^$iXk&qGRsX%s&5lG#S#H_$*u7LZ?H9Fa zy!sR<6yeg{*}2HFTnHb36-wC2o~w6EayL6XAS>fNWl@i6sq?Og%eCO(3@n5}{2{Cg z!+vWgyLjMYm6`wHCwo&JKFn`DHMt+Y6gt&-t^?j4N=8@8h-;nn8VNZbPN|Fwu5&C~ zw10g6$x)2PXz|l~=-6tEn+itTQuJTf8}rRMcb*^kYZEdnc?OwR6MYZZa8fId+nZ}m zGNkFRomj2SY%WUv6IFx>tt~5rahHOGByun&pbI%fbavV)IVeTEGQ4tg(W1q+u?-T3 zn=N6@x90J^61c@JZtyEKBHeAc>5ExXRV%)iS{4!0H3&{Yle|Z5r-N@}77B3ldLl>x zRAJjgE;MIc6?>KMrwQdMFmGBv0RZBOtYc=S_BzbpOxBym?e;~C#H+kNn^a4ITSB#v zxtj~4^xQ%}Ys}Xt2T?4C4}FF9(BGz$Y(--l6!+w8LdYNQ>Dw}0TaejzOZ?%w5ZYZ1 zrl;r(aqpi3n808ufpw4f%{;jB6O#FTl&o6}(F2(vnJ^inDEjz)jLLZJ8PBtfFW_&u zXnXDEr3hQJZ>rxzl0@nH`I`DCT4UGTP}grkZ(hN>;dZ4+wee{U*PTiqjs|ek*~Ec6 zZpSwJW=`WtQX~ZwR_ZmOFAML!(kQDc*VN{XeAnQx(FjSNUAX0| z!&#iT-{&ZuGXLB+JRc(%^U>^~r|3gU^pgSSgH$(}@*DhDI@KE) zirJmimbw8SK0KAUbz``uZ3_iMY@v&foRo#737st5DB<`&p;UKJn#=fVRItmVW1r)* z>=uG@n&d90Lsbs=R=MFn#+`n+&$FfQKSQybOA;VcnbVA_9sL(N6|Qu5l@o?5tYa%- z7VVz$r8%5u3M+YOBByACWryEuC{8mYK&`!F65T@U?(d)PM1R3_a&mH`{!NYiYT2U( zJ770H#2L;U*R{lZw)L!-}Hg{FgE~a^B!Obf(_6(576n-qwU77zFzs`U0zXX)* zQ}==_xZ$?FzT5-t9Zx^8C)1hKn@Jk9dbH_OuCC9gi|V$)`OkMpzvUk`GguBX58OPE zF)MGzM2Y&!i&Sd3TYq;#R-T>`y~!q&?98>9vwi`nbAt>Qrt%by%>lvh3&&Oe*yMn$ z&Hz4>s5?Z+{@vkv&dSy5NE*RzJz1XRB(b?O%XPFvAYp69_}D&vN4xW~pE{ z#+Lu00C!ACVrCbodY&%+FX%o*)MvS}RCT?dMP4oUv&S&y4BFK0Y5)1!*n0QPxrN)K zKA_(4uYcM=NHC2^e_SqGa`I|$P`SJV_DHt7fv!XzB4-IcnlG;}vTJ&>4zvHwXEbvk1FDq zUOB+xMeh0o(+{N{;$QDs?8Z9OjYYlr%ob7iK*{cns;hP_yU#{u7rU&D0~ABcT|qP# zX7IN#;&2Oiw_WHZ1J9e=PGLHIXNSLCeQBUbEDJO2zVUJY(Z%y_-IpQ(ORpg8DiuE) z)A{Vg4zr8Z_&}#G(AwzR^36SrF$Rh-jXs-)S?EVGYg zwg*$lM-D0ab?8=QZ{7Y@xsO>+JGOuD{qS$;df=J*)DGx9Og-~#H*ZX>*+){dU8^0| zi%RZyK@EQq^QZszY(D|uLHsDE>!?3c4A~tN7eZh2Na;cJLq*3X+MPD;$uHu-I@q-5 z$KR);86wt0T3xdCuD5|lCN1u&V?%3au;gT#+D4jR=NjWYAiG~e5?Ev7U@FB^v)Wak z2>lj{T6P#I@v*UrjdAKnPj^? z#Ex|IKS$> z%@E6}|EmR<=5|2VVF1&(-z!Wi`@8!q61fQhVACj(5;LM zR}P~Uac)SqlU%^rQz&8Gn3$KYx<3<`)tDC;A#I;3e2-K&3K$0_&nSf#WGtn_m zS3m;-LsZ+pRRz+b&8+vdQJ@M~;DeHJM_<@B1nog1@R96^dSPolIe5_%4b93oUk$Dk zfzSSJPoq!C$*q3WE(~F|Oq58+^X3&dci%S7>=v)7Df72Xw0g9?d~PP4n9Z2VVC1_*y76+TqoY_#-jLDqDrG}&&-3M}53*`r@O*O!pjg>n zeQu2EndID`_o?IF>iOvKp)8&HGkDn!{NFzXIoODS8h+}(8XnGHP)(14-qe=PrXT)) zxccgVD7y9G-F0cCQxK3wLQ1+DX$k3WkP-!?76Ivy5~M*XX^@gwq#H!(?oNRP7Wf8y z-+S-x`)7%rGxMCM^Nczas8!LLp`iJW1zSxJ0NdtZ!8#mWDbtORb+HC3%UdX}cuE&1 zx`f_PB9XeSt^Zeo@eY^GD7K9Ixb*$|ZLA)>1Ej0thq>Pv;FWji=(QI~xLM?SdWhL3#e(QRH)59`IfqgL+Iv@)Tn7-n6Txy+x z={;sPaWl1iGkFF7dsvnDViJ|WKZX)kyXd3CiM+bp8P1C>HkqUe50@KLqra?%gkgTc z0H4vtmaah4W`Euntju%WTv^&`tl{`MwEwQ~Ybdorp`!S$?=2DlB{*(d@XpJq$xqZN z%uR6yZ{e$w{VG`BUJp#!g73jx2C=28^@Y01y(HU$_+7wT`_& zVl-0Ab`NLlTkIVX>W@rbn>0o=r|Svp$9VOrnJ>-UL&wCF-P^INKwNeLhrHe|oqzlO z^vcI|e^DtYs2o*|9D8%{!c54A|M&^A;S_rgin_dvi)nUo5EIL+LF;(FjrlejK^WHP zZaZBE%|rUAhn4;!(VH7Itcxj?=C_l5r5Ea<+_GhRU}lE&z2*@5GY^ydhH0@s;_rGn zhlM53KdHc%M)tjGQErU$3NV%)y;p_({Z+&;FBjH9u zFSApRlaW^lYs;QAOlfbOj^mF38@w*}*3@ia;%|&Qll3lLOkbmw!=0-3zG^hNIsI{b zK~-Ng2yb*f(WsS^L{=ln-uhiKIqjTCMmyT67y_vujmAMr>`fh%-A_qB>{`4Frp)n6 zcQ?bn z3zNd#XJ3O~p>R zt_f677CN1L>4)Q&Ib)8#l;aXuxPp~b_*j|II@`|Z3*4~lBERP{NtxYiq@yj^xxbmD zMhLSJEYF#Nf>?TJX7an*bpfD`iW7E@-}aFQn+V6`#pW~BA4w+DSru%hL^ z;raaGfpJwZVB0Z|Ruw|d^x1w?Y2K@z&lK{fzTaY4h{4Q6=C`oUwA7Vxnf`|u0%#~( zXc~>5?nE5!u{JnOM^@?l#>aNQ5VD{Is>_!_ukx!HX4zjNqIO6aF2u;ffyoycV^_V` zKrb(_!C9sL{TzenbXkB%jVAWBG)f_Z~#&uRmp5J{T ziZ#u8_cy;b>nLk+$mWL9n-K7&^@+|kj{uW9<2{kbx{Lv6?uuKQG(#o&fcL_V{5;at zdx97?R?;&^ke5(ffz{b4QDTk5KDGBs9X$`Dj()8k)rCGZ2s#DpLQF#lRS2K~s;&5TZ3j!aP2>WbzG z*rejnMr^oDYtMdcTB;9sSv#_7ZtSX>t51K-{+9mMXP}~sip}p%Rv$OyzR4SPdME= z#deIRND7}bbj(;WEu;(JPd$D7erJE6JtvGh*C`5;EOLg2sx}q8?cHF|j@*!z)+oC6 z!QOhVrxfmj{PXbFg`;5U-d6bZnF-Jv-9%;cM6?nysev=rb!pUS|8LtaxgJo>#id(- z_Tf_3q^9Vx_J@&oM1MnM>k6U3Xv0XzFX%W$aY{sp#EBFl@0sT+OcqjY*_muw@<3Emg zDWcbh(wKmBpeNV&2S)H>3U=|x;}p_X8-Uy6U8B;d$7NWfPh2BYbAmTMKL|;pZn)5Z z0{ujud);g6zgXvf9>t5Zt#!Rd^D`SC`7)YL7cw!Q zcbpG1W^cE5aWQw?_F(mK{P*dCx!=Q|rysc68xur{KMrzgt64bI^!N1I94=LK^|}0g zIpxziC>F}g>cg2*NlLNkqf#wg(-z{teeiA;{5-UAu^CKGN;e$dcd zaR|-~LuV#q))hd*WcE*l!+vZf63E9VYKM%GW0Ho)BYS0|IjpLCi`8{V9S)_zvKn$X zSo&^jkb=B+9Cq1=gyif#a--oXaCeb;`o@n=i;V}}pP^~~?n@zxlw{lOP)MX*PH@QJ z+E^FYTxiY*i&1VM+-^X74g=87TxzQkVe6;bv;?61Tp5>4Y`c)B2Tl_ssSs6_IGk);m3%?hDW>0pUdd7|#rI^U|0;$hg6KPX}g`)o|aYBNOhYB@80>2jUL$8-i9h;K(|D}vV}OafigCgsd?-+?N~f=U49JTenL1jFzSI3b7XNX}iH5(W3GC4X6D`?3RfBZ7-f4#;%!Jq7xt2E10; z0DnS2RpNhh^PY@;8%MGqpI-Ac#D^imy2eX_5iAM9fcGRVI$Cs_Z@FKCCw`wlY*8ND zq{1vVcDq}4{KGuMT0IwU{AzwzLi#w+l@l?(s6N%}xhz|2cNjFHEtRazik&2mj-X8Ki2WLvMbq99|ESjP~F*RLo^GOmJ4gM zxr=!LlMw{N1N}r1mn|gi{FHSe%5ynp3AhSUa4YDbv%S+r#$Y-R&vSbI#?O?^C5#z} z{NZa8V`Cv3;eHc8OSLQPK!L&Qx=Eq=sh?4nRS!hBY)0iiZo`M zT?nBMYZ|UHvcpE?&`L>5bNNhxi?5Ff+=U+YlKN9rHD7`3o-;`{a914|wVW+{-^Bz@ zz6hs&|GT|2{e|n=5*@wjPVm67xMVNU+`;!FvRQe@am;kL9h7B+{p#-MQ$Ba`ejzU-=D+S1Ai^*3O%vUvi0W)=|Ni=Q-!zPtJ`Mkm9B^idpX~k zx+}=CHFfJXaS89E(V}t2m4Ou0Wi&16JWTPN`Sn$<&kiX4Mt-PGT|1rg6w}%2bM6J_ zoA&N@MiBNbV!01fSR?rsg1hUv`TMyV<0o|$`BjXuX|7lE^=D*587+K1zS~@tx>t$w zK-cGdCCY4*Up@Ax7{j8E!AnE;-B2j>2<+r2RwsDUM76em5 zPrJ-QJ7ty-N**#GN&Y3ef|t&2nEIFl+H@4 ztB3NH8<#!NEWR#kxo>05@ry+`bIV;w=d|;XclD54qi7;CAd>l3oI`D({a05fZ?CHK zuI!(wTZ&9etEKFns|;j9-tTWV*7MNm#kEgG`~;_NJ)#k)sOiUInQOiJjA}+d;0keW zsSoimHW@~Jw)QYc@q|zC!rVCeqb0^5r%Q4vtNtqvBmZ|O%VON=Os_Zf1nQgFb#A=K z$DKvHcDSf*ueyc=?Jw#*XFHPlPo=du(uq%mE^}mOr8b6(3p!5p^GAiM?bOM3Qy=*G zBWHYhw<_^#Pd()FFsoc0jVJ&g0QGy!jMR;*|6;N;FmgG*D6CHNLP+at@g&k4_`E^#P1*%op~!(34e(K*4&k-7Q;@ab2jYrnl2-*+}okWptzva;JHznWWAmh#ts z8U4=g;;_K2xUQS^)n%jym?Q#2_j4WJ43|Uyj;2LzME;|ELdXMf%fLX<_K9)cEthc9 z>iwiYz&+|b*!`w5mMI^`vuIefBsCxj=`hZa&7+ad;w=%&6`Ogt5*BDd8N|Uz&2`@1 zU`O|EqRQYbBUk3iN}!>J0dqw)#tuu(ba(74!aZg=@%CJ z+m~_`5OS`fv+rh0O!D?SVm`vkNQs!zwl~=ld(|8B^_0C&jo)8qew5xu4L&%i5Pusr z7buu3F0rLmZQNzt{{o&WVR(5aK58V9k5!`adCz|La+=-`Asw^talQo_P>kIyO%as) z1aOZOO*Vn)TLWp{3eQmI{9SxqHT3_+U zInZA$)zd|w#)L#sm8?pAk@J~#{qjVXTQ!~we!y#kGV^xa5Ii!X@H9P5v%!K--5*&? z*38Gsx8)i=iGyl+@+2zWk@NNqrHwr0vR^Y(MxH}EPkl+nc9j-W_i>ljiPvM<>}Y*{ zHxr@yXhd`IkO?lM>o_)iCT}WA>I>hLzRB;nXH_IX+BH@ql|gA&etE$Dhq?u~YwX)k zotf7YszlxCI9I|SJH9xm4O>m9&NN;U%*`2ZjAmCSA!%x+qYPpWr^JU3u1dzsp6K`| zZeC7H0q-AeV2!Dz8jmEzn_rxm!z5QjM-M51I}?)%OqZcFG-x2m*u_LD-_EfjJ|pb7 zWWfu%F#igm3n;Eh$ZQe;fKKs~)&$3u(IB?HIlZ5Rzu5NzKWG7@jH zZPF&0pI>Z`VT9%FASr~5SlXslomD`Jr?neSsYmsmM#oRrXDNIW?Px7r#zp{wI zGs^3!s^7ozuSOW;2cC)!BvjN_<&V#3BJAG|2cn^Yxc!_Is-~o5;bebyEw}uZ*b=<$4wrJHd)TO$F#Vdkoh1AzN`I5_{WpEoc73 zXw%Nuo($p-SU&zoE-zm!yI+?P9?O0?6;%Nz@EAP!tdJz*T`J_uFHCY{gad9>oU!aY zw0Y3ecyFZik848zTLbs-4Wbd2!4Z~SU6SYS>4LTAsvG=E?Ml|X6iGOu_VDu28O}YfR-&R^4qKkM_ z!Gv**KdH#xD3{5!=l=VnAR*Y_{YW2RwzhVu93po`bJ z}%;TRa$;?r{r6ce}a#-meZw7dpE7 zA4}Z2V77oWHu2xvYF^%(MG;?94fzg(K}HM4Evl8%WcMu7Jf^f1V;}jt(JRF%WY{c+ zZAWiFM=(@>Vo^P+pbMX#{(CGg`%(m000xE*bwbW7KGHm4(xg*AUPa6qcsA`v&`KAA+@O@1I3_ zXw1C4mY*u{ikM1N#%OQ7n{q}tB+uA5>}~4!VOVsW(NdQs%BO~}4aPAz!`l?8OfsQ+ zmUjXv1vf(H%m%v)nWW=j*_;+uOdG8+9OFSMB;n4ls4-A;;MjxRO|xA#z}rw z;G@|u#cGtGYxC?mXOZf4h5W zP&U4c`lnfgfv4Qc6(@q)K$X__)^Ym?)g_DUXgVtJM^2K$f1v9ev%`!*$vSb)W(a4s zM#FE)`hUIN9R~P53?C#1S-4Sp6$v|Vk2Fb;k3hK68j|ay)6+nRWTR+xoe3F|3GguW zitenKB>sX@rNfpL`TX!YO2QXWNwd)39pQ*D?X;x8uzy!_LXeQpmtPM}SU^?@PniR< zV^=Z30O$nR;N`#u*H-f@SD$TJq`)BOPkf*>hhK`#4kZu(hd+`|Q$f@yh2_Z7MiU^k zY5Pb4yD3Jlgsz&6QF)57A+~X{|8{2Xy8=tZ~sTwInv|j>?XVcqzZ-i^ciq z5~5utCEC>t)jtC7=6Pg_8&F^#vO_L^aku3li5co96~#F|F;*wohW-J z+JNB|0f5c`JWhSzjKX($*xj{*s&XFI*U@KAg_rRX9Fd4hW#Bvpt9%jfjJTg8RcGTP zzYim7YAnTGnL2LwYUpR+BlPpF0=~Nk@YY{+ zpwSc(#t1^({51an3(eFGVv(Lyk$5aU9i@yD`VP(u`ts{zF#TYxlI&m3Ut$F_o}B|P zvJ_K9c?o5hUCID9R5u?OByexL zcNm#+0*RCz0Z<^-_}JWIzz3^`-v(LMe$&ojKz{dVQxzc9WJq}U9$4mCrA@^%68c!$ z;EC#EY483weNuN!4k%|lyu)}9-=t;|^HP`Q8}94kwha5B(?3z-P|Li(y9LHBA^>(B zno^4~jsnIn=8W~y0N8+|gRtSWkAP9UTXCt7lcpc5waag zT%D1dbCndCn?GaBej0L)lL zDq}RgoC5M2L)}W@J3ylfiWEvbEGRQf7b`T0rVxb)OTr5MZYOx5#1MeFQ7qa6fl&)U zS58?2e>=)CJp3yix0yIxU`VkmjDijLaIvyYy}ciKqB{}fA3k~mO;%>CeN0C)IY&89 z)nHYn?cu{r-uekZcReEjPAIj<1j?O@al0TO89#Q%l53e!z5@)*0=a^WR9OIw8W1KI z%^6?sj#VY}+#z`;1Xy@BiIt+rdwVtv!=0f+r-MV$n?UM_U$HXiZgzil7cEU;%F7@0 z>Hp_Zdr~y%&jO4vVNYO;d)!)S`2lki5AHkQ!;ckrPvul(`kxCh?k>>% zzZc{SFseU*Kj|e782?XF)YCZ)b0Ogw9-T5J9Xy%(+M?p+SZX{;A3&jYZgp}Fx!YgR zQz=)>A#0mu$B??crQ^QMyB+P_s(@M77nZ6Y2yQ1Q{}SX4WEAx0`D=gO8~J|%OF%uw z50}9_Yy1H9?bv4705DBEOMrXtQap)-Jc|TrzV{<0z?8y8(E)~y@r~63luLU|RMuzU z7$$%v@j3H=1D0TW&d9R6RIY>+16*o2@;rruOB~~bAk<`=Dc+V7 z$UQLrU+JWG;s=KCL=3+J`lil~+v-19l0dJ+09(A_ZpK>@w~NL%Jr!xt;qlJ!BoXN3 z{*en%5nP0Q%gFzs4-M2@V&MTyq_;%BGQ1OH>K=02@E7Z|hY2BI+r;d3!}-s>;jqDrs+_f_up=H)pr*~oBv)S4TzoVQ76#X#BEAG$9OBGw>+N=M_i#)3>htElA)P0;WVC6`nd9zgdo z9|#AZ#D5mN6?G&8lV_;=a>9JLJ-~LSjp}wK>H}DL+RxhBwu%ICT6MWY7&-W&IpViG$E^So5CFTuu&Wg{Edh>PFeS9Y zkfMpkAY-SJ1sR5#b6A0QKybhR%#SfgKP4r80&cvwkc2fpKs5Fh%;vf%IJ>JwU3B&B zqwsQ8_;=5inW*%B`P<+Tbt6-&@cR7bXAo@q)~ufz&=V3toK$F7VA7yUNEUkdGo;I- zD$r-hL5jY%A4nd2pmaR1pwoN#%Lt_RYuy>_38ncJY$>9M^z4Y8eG4KgnsN39j+pSa zRWx~o*MiPD0^Xlj9%0jQ?mCNR8-2{xKvW->ZJPrP%UT8;MbgT-E zg~DHUFfhOj?IRgMQ}@UU#nxxWAqcsv3<%Cyq; zv;GK%vsQuYtiwgT*HuQO!CfsZ88^JJHBc{@3>D_^9TS#wjN1{Ezgl1SBoQ3>d@1f# zdJfxbqQn_$?yS}Ps88Zubj}UXqliyAAtWNF)7V+57Z1Irw>j2HNb8mFe1QciVC#k; zgZ3CKnm(2s2Gc*})*5BpBd19?ds47NVGGdCbKK4vT|WVjIS*z>W*6I$Nen;-5~&w| zu)xlk#Wj6bi4D|P5m`eaCyzinV1$}vjzq4(!>*?dJ%{T7Als7#!3}eGC+!y^6d!dP zWr7lAB6&x&#J;&t3WF9+Ox4Cw>g$%-LYR!0-1_w!l`r}n9ml$}WZ!EyMX~1{=Y0uI zdK}JJ!~0B2BS?K^T`%&?wk(e6sRhw@+*w$+6yFewjlkF7kdXs`Z(!I_MxEGbiL-+YC^f z<ao|0z8ryw7G`oXckV@TwLCncG` zlSRYPje0V9=ADzP`FYsh6Dcvk085|;d5Lu&*AteZ25bYdOtleU{3n9@c7;+6{5Iq| zq3BpS$zQ%97Mnt)0-c zVq)!6u?CGMGUs?3SSC^Hr*c3{`_&jg{$PZmo&&9L%()wjOS2BwQ}?o5b#rC>c$NuT zKLL`C(5B-x!Q{}a2O#U$xwg3rkJNzngx1azE^jK$kSYx%XBdi+hTX}hGEZ(vh!JR0 zN5Ps%-hDgwBfdV4s5qN$bcKhCwmLsJev1kFHZ(=ML)j=q+acdmq48aj193v8mM z$Py(|&nzY&wtj-s?+}kWLo%J(3 z%RW<3QSZ^+Zg^8iK3l@9mCZhqI0s?+4o1jV*W_B9+@E@kR4qxTjSVABzKU{k9$R9n zc=kw@k4RbWuuYI#Cj=78(n$fyVu-|e%#38-65zP$6JQKyYq-*&{FxZ-YFLf#%2AD{ zAuzH!xFNNnLt3gV2dLW+bcM)pX~fDx9Sz_}auoCl-dbX2sI6l5M9#Ocn|-G!SGDS& z8uSl3aAhA=efyh8m5~Q5xqHyct6Dk$F}pP~cLfzRAZK>{J@FnCD8##B)&tl;6xN z6H6Tp!1B?3yz?CIIs|9{_gWcn1H&+$3NotR-bWMZ=62@-{B^_Hl^|@^{q4+-kyjr8 z>RC%#r9ahQ3(mJ-%N5aYk0D&_9R~PVMhQl~s9*GFOyl(%Yqn=#ke=543|ppRA>AS2 z9H}og(H(w+hMA(rvCY86fai_J52g1Cil_tPFnn7J!H+czp{iLpu*L)O}Y#zS3u<^#QQT}?SECj zfUNyBpQhPA>54kBW6V0Al8CL^bm|boAA=-8ei{~0&#ydHDixTwe~Iu5Gv;_{BFhDA z`9UdK_S+R(WMjjN$fJ9vqO>HGRwdNOohKC}st;CA4{%{RMVuRNK{i^8>SG*BpZp*a z;LZlw7BE?fJ>H1XyKk@ObiPH4J^vQh3o$(u8QVG6)@u+TJ^g~PXLtIF7?TZe>_t2Dq>%hMNU`qJs|qT zx|E5y%WS}0b$!lZ1dJsm<=rG22n9F7mIFL2he1!W<^wkUc9dvs^zd3-mIwueGGqtx z)o#^#<;0WNowXYBF0ey!{@wv5+zo`3df}P6s`ALWKIBwzWc6u)%Bz&4qy{6HPMHk0)3PABI2`a{RT2iA;&E_LgW@OW~u)5 zE>{702P~!zT>q{gxsI#9y{f)?jlMgsx`=^SUvScTNe zP8B%o=q`Qn9ncpQSRcFG`AG5IopY^%_RQCS7Qoz*$Gghnl-!PX_>jB!)()rAxbIiL>nqvDWIYONT$ZJSM zq90JnMAX!M?LyC_BzkchL=$@nmH|499=>Bkgx-mEtl2x@SV%xQop6vX8^mE6iEFNm zGy4OvZ()URj(xvG+@wgCyt8rw7*umlHVin>IQG&k(6jO8 zxi)OK_e&GjbQrxj`t=OSIr47gnRH*0I4lFdI54S*-oAu9!JDF9y_>T_$^#^?O^Fvw zvYgCJ7E-u^PUKnb0yqk#uiT^h?9t@w-R@FE^fXTTZVv%XOY66^TGR767}kYn>ie+4 z`p_%`cfoz&Q;A_E;y_%%ij`;RRIaun@G%Pn5Ph9Jh;p)v9?HRdg*0-wL3$hAs70R@ zA3i1BD88qe^ng*E(|PPT8kpd|&rx}v62mVMCam8S4mLH!mBorD)Qu1zAwg<%5OGkjLME&;7l?OQgUew|4~pF8lHR@6-NW z6e zJ_!bpyCK-%00+t|yY3N-YX-g4#3GGM@o3*dLAdp22^(S|wYG*hvJe7WelTsLk**ukb6@}5(BeE``FzjISg>Ei zUL6@J)nkz-DRIbizqdw%3G)3TDY{hZ1wGtse zurvXM1jF{C+B1B7PLS|SSHf>%ci6f8ZrB*|*1p=qNtlbfLyakKQt}SwyG0dvoVtCw zoccY*=*)iBvU6`3Zjp3*Pl;pnv_#7rdNjsGn+WIwS&75Zg3ZdRzYhbhF?M4rgWzec(N)aD9VcX#QufGv|wxWvs+Z*LDmo{ ztzWVcDPZ?hN(K0EiZPiH!$MoxSDyVk2AY6ULQc{3w25O&t|=cO(O35Y24@uJ$h z@4gIlcfh4TQs2!KlB%uU9H?Kid0YI_&J3dO*C^GXY}$){`~MktOPZf8fHvzyNd?-EPce0uTS`^Cp#=ZY6?*>@C4Ty=^L&+v)wmOc~he)AyOw~_-vC>4) z9>J0b7Ql#hdU{;m89ODWNi)SP6lm!)si0>@?gpGGMi1XD<^SF0KUjuuK=DLFsl)eU za$g;Hf5JalDwzzLc=#XEsY^pp8H*M_Pki}8sE%YyPqDcp&K>Pa@!?O;L={z8oEnTM zT9#j804$-z_|xpaS6~ouK3az^>Yw}psGHX{m2@Wpchrr?DBlmMtmz3XP@JC>mkpY} z=kPOg&|Ug)G^OxO7C`<1jGc^?0s_S_&dR@XX3G@txTy;_&Sk$=`2%dpCcONmy#|Ew zts&6|wCXh2;+%n4D~{SN=MV(S_aAVpBm421Rpx_WOI;B;+fa^Yl?HLXDl`)(O09@_*KAF1XT73w8b>{#%={kSSn}nwilb7=I z2uDYNvE zKsv%DgYh3OfkA~NVCwz4{?SjN{%F?U?@4PR-A&^j<^Xd4&m|J<=;i+5Fus*nRsAr4 zI;g`hpWjLw1Ug};d?cLnxZtE$-W6}H{vPwbt@e17RJ!4M8?ZdPKkMmLyT7x1}dQEE9`Tn{nj(IG&q~ej$TiNi;FFePQ zJ^1QK<>->9{1G75ca83p=_@PNuV?1~<;MO<;((Yo-pI&!!hpLt1;^r&erMZV!{PmC z79!=5Piiwpz?NJ@D(+WW5)Ai5&<&Q)VN2Gu!HY6T7kj`Yfv0)g8NKVPYsdB0u`Xpm z6I*BLF#U%V`Vz2}yrE}DmGq9kY8A4G0qaqcEQj{!?*$to0VQ$8YHmijVQTbVE3}q_ zif;S#6!FN%J^oBy8gWC_Y0LrIeoQ@|=&4oRLYfcbVo*s-zU+x21L@}#-4y%I1smTJ zs3UpZmv(CYB`-fMTnXvVFliTNh$rr>c63euHJ&nMWkAtMuU%dH;l8pq*#!e1S5h0P zgTFSuOD>;ISY}^eMn>kJ^tzeY%etN`l{kOuZ*){l0d~N>mE&t(D*j_PI9VXxOy|Eq zSe@p(C%{<~vpKpkPVp<1m5VpO6{D5hF6mpe+kZx{*0nr48dBuo(23h;@Y0GqBi3+z z8MmI`>*K6&G-v)&(l%Wy2(oMb@~u=Hd%RC&UD`{s;p>JBu;_PP=RJ)Pt9=duh?7z_asgI$mgq? zbr191D7OV*)yUOg1OFO4P7=tOqMN;|!FMH$5oAC8ywT@V#TRu}c}L?V8MY(a5<26lPQb=qrtB6$zpr|_UR>=cX0?(3yH?Rt9`r8p0D93o2rV}Ip|sINfwW}j$4l|x0O;u zAF-)io_(7a$7#p(Z7Y%{*BQzr;d-t|Q*@_T_2Y*8epDT3j}BWihGme*EEOH^93GBviOk-nlmZy35Em!A<78gp+u)SUgjyCQxUD6JM%-b8G}XI}DTcy$a# zGQroJr6dlQWgi7lpl>iPCej3a!AIoM*L_-i-1**nCeo#T$tvUHA8{6M#|wvDQt_R@ zb4P;HZ|Wvxh=r!GkA5>V=nr64?|sGU+793KQBlq8U)Sseo0Z`%VV#xn#Q7BH2B7CQ zy~aS7a2E`sxDw6M_0Q0u9nhG6-j2)R5V&RLPoNzA*xS!VTDsf}W2+YVCp^>aWTXtM zS75SFMvMkgwT|Z6ZA{ZC;MizlH1&(`V&ieZCGLCO-Up&}jBkt&N89xrJTi@Toz9R# z4I>!LesVUe9|#K2MZ_`2@J_1_-l-fwbfzkVOh3ShonjG%M@YPdg@tmwF`@8uvJWIq4B?9)9Hm zEP?<$mvMt|L^f{QmSC}NlIK~QRFF0nX|2J@%6Y)UOY9bn<~Ao?UudEPXs$-L4o`=a zq>uD6Q6MG@t*vu9@hiz;plpnSbAOptC!oi9GC-*<7+@1bBQw3rJup?AOft7n?28~M zJ*Nt!!Bz#(n=0vmaoy^E>4Falr8d4mM6=A&29y$*ev5sggB5i;_WLW`Rmrpgx@SBy z=05U?!^0qz7T`xBv+0C?wK+inSAd05Gi}c3W{&o9SfxWBQx-x`On4~a);`~vk(acl z3Tu1=c^2df08<42V_R-*EC>t{QAb~m>MN0jl+a}%>}C^$X#RB42}MfL(=*89ktBa)^73?5r1QW(_wpNig~MS_spdAu+B;wP zDuRAUZ^7pX$858{F-`MI^~Z0IMEbmygY&CRT4xmpxxufST95tgg}5H>kxZ6&d79F$ z0l`aya@LfOPu5a?-^T!p%tzpMT*YVm@hb)lXJch?BH!pCvX+hF80PC`&$zXrD734_ zihjD#e$sfKJb7d+B0lKruTc|)&hXTS7*hE9PZZo*=(nG7KPL!_>;G2kL)()dhx9#P zu8pTOY9A;+4hf@<%lOIXi#WiZL)XGP5Zabn%m)}p*Bj63Z zJ;sVuFWSkjbT^QIHTHG&Z?g|xU9Yl=9Q`s|IySSfIeM;z{N-*gG{@ed*7XDwa^nCA zr+$Kp(VqlbT9;I$(rIvYi5n-z!b+lQN9VAveR3IqC$9GR$42Wa<>`v4>?Bvudid}6 z`#bxBui*!8J8*mCCU4$K3F7n(IXwG*mG75T7e(ink-9QNkCh%n@z5M&M60P_->983 zr_jW7;0@!aaiKG3=>Fos_S^5?#R$7IB;RZI@XWlyv@0rpKve6(^fRGJasU^@1HKd2 z@;Fg9TZcs%b?d``qYNT2v`WJO(kMy}9SVqm($Xp24MTT2NXHy7zh#cFI?O{Gcm>ILP5pQ%tGH`mKa9 z1&zZP=kK2SmQPQy_I0yDaY=ub7s2ANy^4l!Lc4EH7sEIco^(jiaZBrXkKwx&v>+t? z;=7?izh1%w>q?sW`(8$`ujecMt{a%Mi5ULS3X|TfB-;zTP+K7>tqS=b@YK2RH)r1l z3IMWr`6dperEBx}8pT!82w05&I)=nH3g~R`Av^1X-ya7^#pA=M4g~9eJm7qkV9O0s zPqTvW@&5&9j6p`^jQ6fdQ$!vK+T5oG0z5?Nqbs1Cu7rPu>;I*oj|mcK5}L(y{99Tf zHx>uYjL0|t_yI({e{~?*6#w4;90@%QWz9P(T0?W;a59%9x<7VKx51*;S)n*-Dt>`jnfKtPVE2Dmvv3XlKQ_DVN^e8_=)K>3T z!_Vkqew?DFcnJEQjtj^bG^q`$k|jt~?(H6)v~fQym8AcHW}tfA?H$_tq1iwB37m=R z-1{W3ReL_dPpb1I?1!J*TyW-WYK)F8%6G=4Ol)hkpH--Pm*O9B$O5v}*x0Yv#H89H ztnt~#nc}l?Od-lW;qY1x_1crmJLUw`jAJ*>p* z7{-2#qM}3^M5wN4b|<`;hKiY6qA`U8S+I4ub`;j*E^ib79SQXPO@f-L)#-K`G>z5C zm&k_15S4nK2W(MFcAo5)b=%W9Kk+t1HH$JMsahY1oBp|iS!g^l{y>~!HtH0bfX{gB z_pXv~zDFf&%)wqF6=2yCl8@Ev=sAgHp|=Fa`)i&&kKI+F4zP&;&|+>;!PFL2@-|% z_uV_s_m_j=t>VTnE=&|}5Awuxm*h>IdP2q{`p;kKv|ijNNXf5Fy$Qt&JT<1~mb`Ej z1Z+wHZjw3mf2o|?={O^#!M7L{%9|K@%Ox0}j2-SK!xL0?NGw|WcVld6e%40{ehu!F zSDe<&%Mf>sT#7gs;^U+_CEyGsq{WMFyYbhx{Uqc|o6O!@INv?yh%IP@4Ti+m4jLDm zNi#wwH@T{Kz>Q0+07kGU?DpygCV=&AD>E@HlNvH6xbx;61vaGa zRbH6sTjzZYPw7%J*T%a)bp2#_F~QsLSv-^KNxgW$s_Jovr(}S2JLDJqH|M|D*$klK zJ2YS4Qkv-c=%P+34@IY@kIvC-GkZ_(sIVpKe|FrfxcX+5+Z}Kljl|hTvk`W5Y{*FJ2FBsddMZZGW z;XUdYrU;yrvnPN}XEYOrH3_Lp!fWFS%BT%e)+To1hq}eMTIaxmqfhXKEB(Pg!kN7dD%$sT0PtvCu z<)qLt$oB>z8#2)@AH9uPoMdXhS0804Zu}9>!WxKk?0OXxS)n13A`VLg_{9G|N&#eTt5xuRT^HyKbF$i}9zq@+lfnUkD+!;M^z?{Wua8d~0TNW1 zItV9ajDU16dK10W@9<|>M?hdU~CF8!+$a-vC;n39Lr=as^LCL&(TFYl$7781V zK?uJ26s^|&Ywz8><>LpchMUFBMTDO_t9EnY%gHBW!U-#N<&J4D*`?d;p15SX@U^YE zUcwo8M@j@xXQntgHcX^3B!{T!-BXg*CMacI$#a;x<4d_S?uQaxBvkMz7)R4%ebi#I ze6B>UP8bzhGa<3c(roeW_ zUEEQ0(A4_QIe2c$i&sZuY_t3)eicpgkg0U$`AM(ki#1VeDST3UM4cB7*0{}o)`&`& z;cf34DbXN^j|G>L5i?hA*1fx;~s49}c*1kK&|LO6Js zn~1xm9O${8gDYn1adE8#H#0Xgvs+Q{aOCy&2r5tE9BZ9`X-#6CkJ%StohF1)d$_fa zASCCDx;<5|I|L5W-;*$K*&ph~d;OX72$zpIImK+z@nY0_w0Ww}T)iK+6`Q3kS-r(F zA$1u2t*k>6&mDQ0DYS+3=Y(gNL1hqdzLcJy?wYaQ!tIPh)9lMwBpyj;>1Qr_FNJ?y zBbPBPgQuVH2XC{|%lfxYJA7Lp6a7qSzynXd_~&w28wc`37d zCgY+a?{RkBBPUrpuo?C1&B@Ma96$34&bfm?Ctv-(_Z?s9{vfrQb5Q0wVxKeA#Qsyc z%hYeLb0oRx^@9tkqqZ;|AJH!JzE)zV&RDViM7EGy5>Zfo6Km(~fx;v&)5mxdein#6 zGPxu&g{X&T-Y)forYoIk_|H^8DZ6fLI%>|HF|c_XlO2rR5<@SHcZL!g?3XNgb=^!U zA)zqoa;EJ(y%tkQ7)OE)IVz#z^Im?(M{>pQJ*(@~h4X8IrC&H-s$w>E8~sv$mVd^m z{s{&UbY+t?DR(6nZhRkr?Fzns@cZ%tj~CDGqHD@XTux`WV=7^`z8WD{bh5%HLs&8N z(yMLO^Soh)|Dyzr$Au(?bcjXNuXVg2FS8JC-3*SjRZmWmpC%hamNFYpY@y{0LEI~L zeugDZ+}%*wnAbbIyri?~!C~R@pBmq!Zb_#wgx`4{9NZe4~9b?&lXogiEOMum^2ov(kX-8EaQxc=idHlbpU-$7A!MEaMUJH z!AY1sv&Ggc@iLV73|i^rQQ3?In5stF19Y`QFTO$0&PL%$P_mj7bd4jkqdG}5v>HxY8u`W9XfE$57wq{|fI z(6KL?Aq5&B5@w0cxWKqH=)36-1w}!mDJ8QFaq#A#{5>L3hQU*Q>c{{diP_;8IIo*; zYUXaaM~^U_7Hg0SL`V9M^jAE*(sv6HqajXB<>)|?~?Tc{AXj%a|wN9dQF8LCTQomv0@a%+4PscfLm1?2emDOrp8XRKln_}nV7w9t0U0(`THX^lSWjo(w+t`>?sby zVP^!CqV~v)jfnAXU}F%k=j$^swGvy5 z^uHNqhGvrb240v4_MzEQxsd#YB(9_@pZ9(!Y;8gA8_y7NDW~;ft9qC_i?lR#Udwbb zx#&c7c&0I4k7W3^Hhc_4^n6*PtS1TogjZ?v*g&pBc8@WX|F|l_T1SjVTbLn-H%J=7 zxvsjDBeIfK{Q1NS3t&7s8@~g#Q;dC!QAZ{6Xk-YFno4GT=JQxi#fSKc;d8CCY9Akb zVq#BT=FV-?utIw62VF!hjdmA*XXNV1up!s+Q>&$U@d%?B$okI>$neO@-@c(CBDLS{ z-QSO+6_En(K+uTuyHjL-` zsM+s$8sV5|^o?hF5e?tiluI;p%s^cdpj4eNC;7t#T-rekZ`yXC#=7m|BUZ~hM?7v+ zL0met*_wC<*o2QEk2sN{!mC7gAN(=r7R*SYd1&q7ZU+h14X8eeC90rQ%yoxMB|JFh zuy8KY%gyk!t>O0oQ&++Ax#K?BN5c!haT*qlVw+n*(jyF?B;wK~#3qdKLO-%q3!EyR z5~|BPzb&;*`}1|}#YVEh>cd&Bi;t*ftM~5SEf54CR#vp=BQdPiW~&7?}QCA0WL zq!Kb}@_O#EA{18rlI$0ABKl8~;*n$tx1^oN@@=wgGUqoq*UEUa+0^gxP7duJpk915 zN}MW|(_ziKCz+&ks;Jna=0eiUN|1N0qFaKzL>L)_zlQ z;NpXQ$BsE+E!PKcw1mn$_nsPxYz>HzZ>UT>ZaLIIdmGLpt0}j3& zIXqT*IS@@mHerHtcb6pMyTjZFBeVFZ?#p!8b1P1*pQ1+*y-|S*5>2WUCeraDp#;He zORT@IZP_gLFd_CLl78^^si3vFxo7T0_4f7d9y7B1z1elBPHXfARjct6A7`oN-Dn{o*PWp zPpRoj-<8+k5nN1;Mt{vR+P9rz+iOT$(!DeFpwE`-+S+n9Ynu%ZAO9JKM(cA`YS1+X zfuK)G`g*@u&ty?tzH96ovSml@6V6YLn~ctA-z+xm++j+rfH6@J^A^${L7807U|3oB zi;G?k9WP{hX!#E)Z3#a&&hu$?6ulXCPT*v3Y3GjF90CX$Nc(Q`_FnVVf zojh|{@a|39nw-C%dJSl>l<&VYt}(xCO-LJY{Zr~-T7m7K;Rg4|U8^i*%|4^{^5ClX_rNsoR0AeoG|v26l*WGn8@3@ zD?STnaC4lyaavl*8J^21a!=^1cuY)Nqq1cENSz4MBkGTzJ}GAm#UEb>MB0|)OR~i0 z3=(7iCB?qpq;zm?zW(;7V+uo89R3%2*880E>r*>wrWTu%?|Dw->K$jcI@cDYpX_Lz ze3iYaO`v}>t#`ejlYZCsbsHnz8J{8iE+A@3AW!n*d~f63=)cKp0u4rjvMJ!u^){&` z5!b$0KbC&8Ehe(&6^{6Pb}qqc+yJ^m0E$Kd?1>5h*^8FVtw# z`x@SyI;g{L)052fj8wwUsAF|Gce;wGX=>8k8 zOTR0gLZg*5J>*Gl^d(l}$(d7rbJLWaqq<)bTK_Dr__gbx#iK~g`=te~Dm$u6x9QpZ z!UN}-0&mYLG*GxAhxr^tOG+y$<$gV<=RM*Pu>7`>@J!~+KlE;~No3x|E5y0waaf&y zH9Tmk{Yyxs=p8WpH3}y2m&C{Tx_S z1L;zRS(hKva#tUjh7iS1J;g3`i?(wKyyaF3N63k$ZkkSP=R`-9`=}`s^DA86AlS2Nq3EUM`N~oJvYQ9eb2lQ` z3EE*!zWJ)$51FgS$RDoM3C1u`8CHCVTQIlXDHRvRx@AmV46kR=bs0Qddd!SJP@|DW`?v%zXk$FtS~WYovm|~xPF0j2F?wv@Y~R0oSo3w*j{+-u&Ydwo@{A#pEyC)0ke8PS=_*lbg8`Lr z>8JS!5t*~LcN3YzIhVol@p9O~tf*e!QbSAJIhGiwLko!6DaEJxK5*>PVdYqmBQ2O3ub zvU?DpkhNLPFlFJGlxB;eHu=s87L7;lyR_0JIz6hn(KQ?k*s5&ZxM+0xTO*gwDR})) z6XM%;4<<~w*!-}$jsYLY1|NS(K`p|g3tUnz<$Bj0v|lx524h181iX0Kjo86RG3 zvxXg+#iLu6afPQtt67*r9?7tJC7!Nr1+_O1^*)lhvIQ^$dXTQ z&sEH%L-=mwq%Htv88athaZ%Q%8bHb4A%&l0C7w(L3Igqj8imN?1YP=C4#S41&FAMY zOmljz(udiY+sZ}I{5f)#h2IV%fw{Vs+&T9~I=weT6Y{O5UEKeD$WF7wrrLrlV{Z1o zofF7gj5@n8_x_x5`VhbVF;=JeOHlA7aphTg8T4(q(ZddiEs+Rq(G#&@$gKyU*>ku> zgea-VUxN$J0$- zHx3IngYLWeHLv#P{fU#1NsZ?=NzKs9YduK7TDohs!9yz$do;MlCM7&m6=Gisv<`gB&!oQk9 zZXx7Jc-+!s<*r35m@+0^5&0uY3c2%XqGJd!RUhJ~Zlp>FB5s_n2Q=Pt$@x!tRE5>a z4n>aAH$bHu4h;o8{5X?boOzsd_o21&M$x=gVHW~P1>qOhzWAQ;*6RMX8jgRZ&_Q_M z`mndf!Z{E^J+cBb&;w>b;6|j@eP=w)vA)#g&;oMDA2?i1j75fL7CLr`I8SnEgaMiLvpf0Q-w;TQ!L3CTW@JWnH%SpjMEXgUP(i5 zDkDsA38T>s-$Ks;y2y~4rBZB3H7{Uu8LuQNkw zv(_if7d#r3w%ETPbZGZsh>^T%P6B)~`#&`~W?b^@c<9$oHYMYEXU?oQl=7a6kVO43UqS(WRM36_i(-PIz!eGT}R+l+eM8qnFhQuLu! zNms>@Dn>w5Uw5C>ID2?`*EoQ{SNnIBXk)!O@l$3ov5U{vy4V+)h2<NOzs2tiJ#t5&Phty)!vYuHc>|A5aKXepQuf5^(o9%W7`Esz;LE>v=qeoi|U@{<6T zf}){54d5nAyAB?K`{U$?`GWdUDONw7wo@Oh{&u(T$*!*a$f+2o$^GUVUToQV2T6&Nf1J8i{n`6-6mx+H z2CD~F=kYI-vfm#D^ue{&Do~5igg)i<`;Xb-$8`>o`)RSF z2OZR3M88M->s{3m*UeI{Z0p_3Mh&IpetJL$`$AB;0V)*`0VjLqjpEXwrg4o8i#pE} zJr4D5i@J0tP|FWOx7)^hz0OrKYex38v4a_fc2iOmlKUACI@pZfxxdv)>bT%?IoUoR zSDe-F+<~&y5yRiBB$^L>3+wSh}%uKmSr)2!64j&ER1bPu;F(+iR&9l0S zPO|2bDe`#NfDJYAUped8XSpxt+4~|;v|}3HHQ@K>QA4p(%a6-ONeIg<%8^{&Zj2qd7!U*+3FophN=6mC_i{#X0PyjO(}R$mgP`05EQ1v8{?h1wH}#(NhG*d z;{&{vCZqwhSWaJLTF7W;M&>jk&rgK3=a+xTlKA(m7NC*y!aS0S63L5i7uGyg*3{3Q zdNPlVcJ_E5B)av5tyNc|SIbZ(tE7^VF`jJRQYhQ6Eq8-&p8duftQ74VjXoXs{py^+ z*RhtPC|F{wZi!*AJ0!6~b9Nx7+n0(5990B*G z)*ju0dz+*9SiR-QbXZUzpsXq$(JJ3;lGNwi1YhZEd*H5*nq=1<9X$G!l!aO(e*dh? z*1}+3SdKJCB)Q!G10CDd!H(TkWq6YdegU&n@RO=VWFsgw3S@TYBmIJ#v1z0WPW1ai~tXv7OhWOd#Kn z`M8KEO~v8swn|AgyNI_4X}e(%dwYZr>2*KgrL$+qKz6io-2gNd7QFa%M>3Al%G2`eQbwqXV3=bC^NMV_zR8zs07x~=&`Q_zFhsKS!qx!ByTzM?@ z#fTMYl7><5?E?|!MO@U>9x+> z){I>KhKLvI_>~`QyJ{77L_M5)0 z&nqC})`_Gp5w(IJ%OBsW6Qxa(C^6 zy@R&6{6#OGTv0%9DA4u7v0W)-@~;1x4hn48@NUK>@j1l79+FVS!1l{A1h&lI%5l^& z%hC5v6W~zUQt`14Q#Ida2jt*!!YdQ5Xf)L`8~pGtl~_tc2T_T4qWp7@?&i%ehf`{I zWA^xzx-my-X1G#|ZWas}moGqmU#@m$L`pr>8zZu5k*^Q}zxBwSOg_Hj;xtCuq6klQ?r}KjLBL0ZUhn6|yT!A}S%OR@nr-?;jFxQzQihVBY8S`nL>B)lqqw?VGC1Uqgqp93ZKm*(AJI8#V0US-0=3IIzMIv+ z6;5i6c6}Gy@*8O0^50@;UZC3=qp02EikbO=}E6Roh#)n13 zl&v{)&}amJQv8Yf#<6rDKVLxBVh}5=EkTJN5=`ya{5PSni-b8C=Q@;CPJ`8yRN-%* z^ek-cVuQ5~k2%EqkMP9XXZiaymL;hJl_{vnJdhdeQ3^DKC@`f8yV zer%@%Q{JD7R(v6msbk?~R{1xif>|{cj3%UJ39yu0>Qw&ukO?zMltmaYVJ^tZssNMJ zJ!N{+W})VEw=M-SAW{PZR5iZYxOKs9J6d|aOGG1R4u+v04AWWM`QgkZ&z4uOSmfA4 zUTjt8fP+U>L8;#7HIf*~06~W-uU`%B>t3%v`khirMx($1xbY?-=E%kVi}#r*oM=~ICL9Ohuj>Iba`E`J)v{m`+>f6 zKQ}(ocs>O53P0}L(3Xd?F(j0})cUWtTs4d`-qt4VXpS!Pl}7@nG?`Ot2t%oDt@2Li zFNPlZArbv;WGm$N?Gbuj?-WV^z)E1b1(;AAAs&Z&X^jPUFac~KpMUCs%=b*;%&ukk zyBNc668db>9U`F9?BhOCqU-uy8lP@2D@j!%ojSxhib1A*=XtmCEj~pGt!K`HiEX?sr4qz>)W=R8LdC>>_B(yXDH6|P}!S^+_4K#I;e>^9w3-}P*K_q_P z71^_^tzTf%4$fvTuzB;M&vlU(9~NnU?zncoS8@4Lx_N9egYWReE@wGAd@IF+%(%2- zf^_9oAt4&XKtuBWyUFubx?>1Mu<83A0M(9Nn4%C839p&rvX+d2h4jqK%+_umP;zXX z-Y>OkzSuA8T}JB9M>ONxNm$NECwIM3>0Dg?xI?shFd~laoF<@ZFTb%zHDfF4L+z5{nLLA;AyW%EXUN9=wZsF`w6M%{Ga zOx1Vk3x6!J13HyYY4n&M-??5mU*J1mpyb`Q-)`xCu)qCijXqJmlq&%Qa6Pb_W3WTf z{Ru$hT>!Fw+ZCvYv3(ugSiOvIOT2pP=$Ub*^zl^r38R~nzt;A*wW2J;M!7T=o4dyW zHp?DG#2@7Z|Xg~3dQc8eDx10Rd4>aIo6ONs+rwpvlc3UJvmSbrc1GU&FamW;e3 z^W4!T|1ZafhiSusF{?s2T(S()UY|@=}Jt>dRZ%SpqtvruWeY{sCoVD%&%s`ruc8| z3=(4i0BPnwKw3Eazc}z@U+CT6`tbpWgN4c8n;z&H%S`(sKbDvp*I`ZV4Y!~2)Z#u! zWX`$t8&y{Cl{|LA^+2|0217$<#dpTtbB9$CkfK7F+pgS9g%vAVM*8D7bded4d^gs3 z=9X1C=H6ZL9^O)M$wU!G z=u2i@zK4s!j}zbl{JEdL?zmc`QrUsn?6B}366=DV-ncA;b1jL=eLPbp$Wzlr3MV9v zO({PKR-R@+mAR;Bg{>b|!fRDsE#493=sW#-i7VN; zvjfLZ?W#Ducg}=P1buEPB+2n?Ka;b!tuZ=~P9+xo{Z8&UbVF1XfE+8NgF}4D77GE| z<^8t-3})A*nnSG}Xcyi^xw^VS9_ZMJ)pcuN9)b{gsafpn5HOG*nLh0rzGTG`&29vx z|2@_t8L&`7`ZnaAmknx0Y3I`p-Q3TYV>TIeNF4}C=cTE9rgfI!wy12_^y|x=Yb|hDr6#6mwh)Ivp z8$xQex6tJx-cs&p7s-aoM;*NhmU;|-{jQo!a_b%*VGLU^0h5BMHiY=x*9G+$?)?mf zNofj$?95)yjxS{KuQ%Ms?M~2^dqJR4kJRz+Fz5jA1_t1b?)@3kZcOy#&m95cP@%&7 z(FcR4bLD=vQ>otfe$TJj4@9{m z`1yQsGKs860fufQ8UjaqI=4T2)-^b;~V+ZrJk5kg2`j_ic_Ps-`uh<9k8d-=z2+{BE2uPk7j_heB)VXm#uVIvk#`_U zWk>Z^6>prr-C>2vEFJXJ55=w1hM5(5j>6^gx*9aBn}i)-vEnpBj{Na+Q4IcsjI$TWJ#si_Gn{`6IIRuvRs6hcMN0%M9_>GFk$OHZyjG-6r!uwsg2m6nA{KH{tfR%5 z$|u)G9v+G2gM0g1XNvPp$sW_KKW$xFek5Nq>k&RVYK8J^(J?89XoBrX&;C;8l^d-T zF~1or-Yqia5PjYPY%DS%B<>tZ%HNlI@kA>Z>#kUJhVmJttIxA;b!Id9_ z!2?w;AP?t!*d}NQ^2f#GV0aK%5Ye>O!3|Q6j6mpLq0Z)8qvbr&jRuliN@ugb!j~em zq?#7yqkCJj-C3h)Ol^xz)jKGOV+aG*iM;qh(XEAipQvAzRmZ0rGmvy zdhy}L&Ej4vX}2;sR3Ma@_dJE*O4|r4)l|*Gp|Od|m8q_+`Ly$lHiw8CK$$DBV0{a? zUbDbglrLBVkJ`V6NEInF?U*O`M;PCNFfkqlzLy1mr^~R5;KU^;Ns}t*!r8?BcQKNe-$xB1)-PUJbcPenNG@l&LR~R8 zU5JHe1MQcWmvu#0Z)*Pe^QZL6=qD_RK#{}4+e^d$s)XW0p8l+1x$dn=-0>hPADUVv zM!i1pZnEa$F2H|7uO9pbZ4%UcuQ(;!krMCSsVQ1sE;EiubmPqVrlU@y$3{d^ApT=| z??smHBFkCS7xUD0%L!UnD0x01;LJ=bNhoZhdH9oqd+&hIOx8WsrZICQ@`5nc2zh)z ze|RJw^AhWJ-y1qHC*^-lJ%N<6I@>P0suZdF3OuhC~Gs>C=2p@~P%uEyH- z{w)j2n5puKu?;xug{-|SAUO%4ZhB{ohFYKWx^psKe{cb-%Y=_)h67abgj$aWj7}HX zC671o8T_`{`Z3PEbe^e>$lXl%gAy4_+v7Xd;fN=AFW7%@`8j#A7{3NeXjc9Sa=Rjf z%KHS|QEe5hxOaurWsF|J`4#aDs3b>Ab*uh-7Tc^_@Du{+nzXl|Lx(>SiNwV4wH$r9 z;vTHQ|Lo?iChcpRy#P#Vv~zH9NGfXBY*??U2MOI6c@@WOkb`5=Ej*U}|7rowsLLG) zN+NMgmp@iw9?w-VzsYI(j}B?KsR72Q!Fp$f^rF{{em6}; zeT0gO;dHEbbN_Rh|HcveVz1z^^F)BRU)93Dh+7R>Ohn@_#L6QXqY{r+HuiuJ1I54C zQO(~RhO(ePU>G9?Kg8ITjp-R64i=F-1nbDae?#f>Sy2n6g?c;x2u~h z0+sX!0Enijyuy!g^+LT+PJ8)u_3B+f%`p=F7%@*;y!_`6as9!BEklIn6_G_#JMFXX zxY=^L$sl}jw7_RVmST214gg~5nER+fYx%I&OM_>0qXwS8N3MXj|H!A8j~(KNS)P@q zph=~@(;#e50%vc)X&hd5%n>kGtyzPd=4nW!{ZkZ9@bWho*WaEfBHdbu(!+`^*73rG z8}*!qS&KY8`FnkE%^Wm(zrNZ2|KwWoXXXPLkCtiOen?|Pju(^qvRcfso%A{OHRFG5 zeK$Uj-d{!sTu>ACzhw8eK(WKeP32m^Q2l!jx!kV{A=}t-1k)F-Mg$mzCFk7|A5tlt zPwwu8hDpDEO}{!mj+@M!w`!oS2V~8XJ=pfGM!;ahpS8au;`1jI#2Gfohz)3fqy=0W z;4B{5j?lU;C2LQA*>U~C6oFXI-tt@4;gJNKp`Op>FIx;|a`r7D}?8bIW&0|_yH ziSHr{Lp{gpci}EoR6R=q!X=Xj;Dr9G#NwUR$i!#cfa54tzxieN2B)4M)c+|BbrIZN zG@g&NH+~OM@JTGGCdB}T5dMSgijNL&peNhTWljAa9q|@GNg1xIy+p>S7iIryqRLFU zN9t$XOFnm&D?neYU)8)INveWWpSgLgy?nI&JE^=6)r=cHWH&rC;=q5k@~2myd5R(v z#1D?9fhE>Bze(4vb@-h#bJQb59{|Q~bD$;nQ_TeD=|=60>u8zLt55Uci?~H6749gp!$n-?lZDIJU%fZz^8ao8%`Jvg zU)G)ai1H8e&;sRkSklB#rh!yvUlq?iytH5Cjoe5?1i7mek8adu0|MG0Oj z1lQFT?Y_JiSFGV`&0KWruClmJ1{%*daX(XWIfTo(vDCF&yP%{7ff;8AnjLuF4yr=^ zMK3}fdko>^SEMekemCakEUO-%<}pdyPXhTzl6{XyW44{&I)|x=`wId;Up41YyuiLa zs<v=EIgfP}Di%xErQKBC?qH($J~>dhLd_@%bl5|8?c7T-dgMMr*5XA>LMS#&37y zDwb%D8U3e*^|t@?P@) z>Z?~RE8Mi`MLcm z=AX5-_-(M;K1@oyIR{M9eipUw6+4gw7I@C?uP{RnWCMSH6%7k+NRhF$f@|h)#MsXN zZX6F90S>v^l9NUV)~&kmp@fuf^Yg)vIU!2Y#p!W{r|4{bVsJN?kKXZMF<wcmvu$WiBo%3flZ8dj-@vs^pe^61-tJLY@vskA0JjZVDy< z?ZLd2oLUTRPt16j;!*st70GW6>g&Zvx}aU#9!zR4vHVR3-nEz@J)LGbc%mG3!Q!g* z2-p=EjsB_2<7RHLUl{-Mq)J%38#5n^nD2ocNp}8{g@e@rSG&rIDE?EJ1Szm+vB~2D zJ^v!J>I?4gn%FAg{8D;#UA0D0B;?^>F*YVGz;Z?=k+sP}((iKOy z+dmOit>69t^g{xVh6Ep4;342%QdyEq9`>cb!O8vTnXnQG%}<->AYfeHT<=L=EL#}Y zx-PKN^O5d7qzABf0t#tbnlyO$aFHq~+TV7NOYGMTFk(v>)3V_L&+d%GCcm1Og?j54 z;~qU^O2EeF)u*8eKPM+1=EqyCfm^(I>=5#TncrvvdlyZ%9sdyYfS8uk)?y&zA%0m= zD)+us(+9i*H8 zOWY@_&7@$Uc6cM#SRLLp6DW&#csE=B(Tzr1exJP8+>^0e0*|0h75r}UHckz0FMsd`R`~P3c+vVS(ze ztZ%Gt`_6LzA6bWuZ0>~7>_yPTQ$;ROkm1feJdtd8m;&0fVQqf>OqPGM#S3|8{pYVO z3#gjh6|wj5f#B7VG~FjCw(5;{YKnMbOaX4GelN}=cHZjYvtjkCGm8d)F3y_pVR|Wr z+%u?7fBnZTG%ch6K^a)v00|D5M9-n4P{34GcU^j z009RZE*QcM7!b6nkWSiv|%HweXrG_qtG{%TGh%Ji2Wt8!z zf}%~MKK+73f&^$Gb}j<%+vJOVPbQlD`PaGFLeBN|B+#zoKMg!Am5f`eH$O`ZTE9|R zh(?b*wOF|E3xW*^^wLt~+y0vYl)Bwe$$=KSmZR(k-1h~|1Xf=yT-g6Dhd3y;QOlG? z*N_`q#1A)Jb60tFs-bG(2eyJ3*SyAstZ^fK~ zMr2yFUMa597=dwu9+d7p(AlE4HTvR2xcKKMKq&|o>)YjeE+)0d*g-y@A}mo=PKo(h zOm@uvpK`L;KqOWytShgsc%ND#z%**4hdG_sE{62Lm7p4Qlq_H+U*!)c6Bg_vu)vUz zh{iy)K=-UFG9Mv{)JWaGeP*A36D`EJeo8vJ!{&3lyRJlHn4VVdrsRvGJNE@%@sW-W zru*L}c+ysZzz6mm2oVHc%8RKwTWVj*T+?x;hGYb*k_BwRFLGcPSc3ci*VbFdMbWj7 z!?2)~0=uXf1D7{}>nel+HwO}CC^fyLOb*<*%mEN@~kND|%@H!+1}h;{w-XYxv>=uhIt~ zM)ThuwR&7m>!6d!-B_OT`z{E>KoZj9{X-SEmE-;>h4KLt$^xZ+k|naP745pI^RTa+AF1lI?ZXx$~|)f`HNgX*%M5e*sm=k=ewf zOg-0I&pja19CL?yu}-R4QSDR?bECe^G*>-o*fW9;0`|a1lq%x*C06x3s^4Ze=gb_U z1KE};fe=S*$be6>1eD33c)vl6gP^#bh3XKzlrJ`)JRDDgHIA7v^yX%7IrYhk$to9B zmv9%4%Kf7A=8>w!IdXa)kk(O&isk>gcRO=TYU?v-=Xi}a8UM1#As3ieC?OXh>f*Jr z%y9@(6CtgOF|3I3TZ5Wop`0(;$XX5(AKvmC)co3o8557ew?_IDO+>ew;N0WVd=0Dlk1$}gmbvZdoZD4b6^SF|zvngz_r_!@OZc@B$$BmU&e|#FYCEmJ0S*S9}G3B3I zTrBwQ4`>nj8Iiiv!{O@E2YPQf55Yr!hp)of)`n>D>m`N%#cltG_uD+A1}pP8McU_eI67O-Tn8TdnlDq%|CH?q(aQJXJl{(H`} z+$Nsw(G8!(+0v8%BIWB==n&T$ z+0n`(n}2g}!3+P^v-jkjziP3CTE={O-;o3IBmcc8g##d#Io|fg&VAU^s@j~rU%!|C z(=kp8O^B_d)htuo7sIaO7VZ@56;S@|7l-N;07U8Vwft=j_HW1Iz4zB4{)1KtmQ)3j z40~RAxb~^Y5zh`;VoqK$ojhZ zogn|E&Mb|(CH?%*yT83oAeQ_aZxs`R*_VyTH^VVn2}ZeBKrhh0FB1A&cljDF^uysR zSN)Rfa}tM4ewoAvrRD8ym1Gd;|0i06NzDi&ag&p4zF&rmJgHot2hh+Z#owa5J1|?ID^F-XJ1z2$Sz#Wn=0mBoz)nWo%Wl*sv zJIPK^@4tv|Zn@JD z4MI6YxEI*}3v*EP1Tsx}Y9$|`Te*eIwT9W72d8fSjEC0p&rOejHzNDzyZ+2uaEZKy zT}i3t=90Omi~lZwlu84Xso7jM$$KA9-aqehEmAL1rYK&qun%E-g49HFOWV2Zs`7ae z8zR>#{DhD!|BTk+CApinvxg`A&T<&w&NwF0bz*m7AbS80(?d<}P7=}jZ>}*Up+bSm zbKQeEZ9@yRhPL1wD_4k+Zv}8NP4;bsnw$YoE&%Br$5!YrDIg_!2D079h>t)zTW{|Q zbQfrC5Ac)du0q5@FL~hRm%dwoL4WJ_*Fx`Ff~*poeCF91^1yB7rui^#_PzK26(q;N z^8#_`7oZFo1R8PlOJ1WzeGbq513MAmOpB*-yYPSnBC2 zjS`9@*kNeZon0ew&;CQroj!o!es#Rzh&w^L`A2`(&*@3YUrGnWAqq^c%y9vlY!8V8NJz!6TNe-qKuBeF_1H{jq62*7rQ?=u)Jh*@@co7U zd`pzH76hOYu=^#ZoqI8`O$^;g520jxTld|&b|L96VIs}~Wd3Ym$2L$EUe=V0eEa@@ zpSG1~4H1E)UixmymCk!-T3j4{CT>fS&08&cFvUv2VfUw3p~fO-3(L;zOiF9bWKa zF>to-3*NH;2SaJ}#s$PRZ-AG1XcE^(;0Lg`IrHs-9-wxGMzByRKT-)F=)*<8gz<2X z1fr6#0t6t~QF{mS-U>;B!9Y>D6?9h;l!?CZP1=e02pGik_+23Scs0RY4(%V_nn_|8 z)o13T`|h4Z%y0qWEIvJ~194Xz53ocuR);*usKO^p)jW)p0`89qEfkYmYyFk@@g2{Y zp-=2Q2PSLRcu&U6fc=b1H2P$^ESw>gr~I#UQMI6k2rIyzA&;Z*O0ye=UjEmf-SEc$ zdaMotBoPZ_a408dkONW8PqlmI|G@YtcYmN^5Z?Ja&rQ$r#XrCYVgM3;AtGy(R40%D z^A|3>rwSl|U=I<35CX9OZDxgyLHtYh>NY@Bf=|tY>XA-zHwZ$584@63{eQ_;!NM8XnAkiXK;&34C-IT^eL2rcj(|Lzm|o)UI2n;1xH z-0$WlY9nx&h&LeAe{!;8qZtUpi?SHdZgD00rp?L!kSz`>7a_kfzi=MAh%T1axLj(?kP>1$c?xrOO3$`2pB^PYI3ls4p4+i_wO)QW$v1JB>`HXL*;0iN$ zpqU1ZunL1}MXPyP7VYVj!$UiZs!@Rw~4!R>@k3ZbeT{VOWyy$~E7_$%r&}s7-!8j0HQ1Jp-T$tE==63Q^pg1Cq_4m=`x0EN5nn z^1K;2%D3pl)2s)2->+?>3RH_dZ{789E;3n_Ji~X;4y%vA2x>Xo-!8I>l#R4E{nH>! z!R_fAnHRZtnacMd_FP+%CQ$+Z>$8wj3XqX!&bkKptiFm z+~$(T+E?c`Nz5is<7MR|8>SRe3{aJgwbs}qs-m_ANPzPo`{fEYI*|1p7iE|@ccmhN z|C*txgU)fR!Ztc`xMd`DJ+k_ZkK0kVUby^T`}8RU>AL&D^Qjk?sC#qpT$Q&5y!DGL zPXzM3(^!!GqqIYEd#w}oB6_qOQ~U<(*>`t6cXscDsD3Vfvi)XwF3~2scE$fsC2CZ2 zt;%xh^`^i!wR2+or=jBxyz+0$K7g!Gq+ZQ2`F1T$L$} z#Uh{^rtzS7B%7|2_?>0iYsYf693?XX>3^v3kej3|(M2zp?j%i|cXt5wDhF)*@_Wl& zj6cTw+(}#Myo2S;>rd@IqikDw-z~^q_y4Q|$Ra6Cl1Y_S=fAlC>KqdR-qPOgfo7Yd zRvhxXHcmF)Q)e6K7U@;Tbka2+A1}l|AEWrpFJJKy{(RR_?VH#;(Te_u4vy^RUEYyD z@(gd+MowasCdX(S+Ll6A7kbph?Wgh?owqVeT9I=|GAIEogA4WV4??k&^&_Bp}npEo@d0*WL z`_l^%7AnPozQa-1p{FeS=4k=fA!uouEIwBK5V&xA>Nrm{052|NItzdWJc!3M>e#?O za90+(hCh9IHv;q`jwL~2S2_Yi{I#7EPzBn~)2Q9kQ+n9F;uTGJAlfV-J0=LT}(apL1<%daQ>aX;Qpj%U9%P6B6T&?`Ig zc{+ituEyavOeGBgjJxZ>+mf{fhuT_e%_w=TYqdPQVRw4E2?wZcNh@l(pgerkm(D9jN=m#V;d#R4yDZ9Ua}G zGB-20M6?5HAR3@U#L*2}2GahlF=o+EYwk|A zf^0| z{n8v20`b5vrXA@RJ(*RnQoBpj`B)(s6R=iKTge!{xv(b>?m;Um7HZfa+ZhY zECpv}H7fX-@F!Z~pzooRSudBTt$rkKWn50q*>ADx$~7kn#lQQ#Aq=d#e)U?DI><^o z4IEx;jTk5M`AdP}FStFgdty{(EcR1Y*7oTdq8Gbs8Tf9hoBZs*Aeyz(t##^?V(SY- zZ+c{)i^=zX?Wb%hY-ilm9-p4&n~IxLN**oBVO#PB6;B_%TvR1V#<5n^>eN$*O>;wA zr~bHF1PQe&JUmipYlrhb_lxHEw!6O)MZEF*`P1>-BGU^sz2zg0n(YT)xAGj7^qGvw zRqEA{LU8N&E$)D%mPdy->NTxyika1G|IyOFD8RqI8)K{Yr?nsMGz6PIy_*#}wG*k|Px&W4;Tl;l353ZFMi+ah>GOwubXyYR(V$UW~w_UkRQg^GW5GCczlz z8l|3r8)ELalKG}G+d1;WvtGKpVYqXbFn2FLX215XBiq?uT5eHqU9`75n@ zYLGRSvV#G;%xJ8Ak;jq8ATq1ler#pUtlpyDnEZCWws5eqdL4#C<4tf|uFIZ|_3m4u z33meWe|6R~Ln@R|LzPx>bu$Y!7jCQaeeKZ2PfL{DPVa4lHcNLMiT&tm=kS0F^75f^ zFw(VFj&b{*r(s9!>%f9i-Zh*{HZlw899b59rYNFM?~T9}P1GZo((Ry?{<;lD;fUhp z8|2&(cskd>XU&0lbP!@3v9h9=gxt+N^jVZw}_*kznU z>_$moGUb~d^^Z@|o+_R2%})EI#5Of9uk*IOOlB2V%OO$R#|=X^&F3$+>CTX z2uo3IGlthFCrxnRJ;xJ7%PAWRzslj$KBrfEY_6ahmL%A|&w=L}PJY_rwrYnv1erXW z-ci`ZJTM8CKg1}1>xJoy`L6y|9Xz>)@2s(4&~zTi;RigmkpXh z`3kuNg=;)~+%>^5!8PlGF%D4(mUu~`782fE&G4#K4!@Cbjd~Tcsm1wTz#H2uFjZCJ z&eK&`ihllyJH49WYD;p?LWAyJ>1N+x5ZFEw-C=KUmS&$MUlWS1Lrjx)abdM!m?X8z z$y3s2fAzL1YQ2cZxj<1NHY^L2n?|2uim`Z1j`Yc?`*o?{)GGOh^{-I@l8BC080AdtC*W}Fnm&UN-Iu&U;~1m)7?$P>B-lHTe$)OMehRaZsq zSsuwj>YvyyfrQ&`;yNO${dRNnR?xwuc>?($@Qn3vIZEGzs7VYsp|XQGMBhe}7mspV zIhU~442lk`(Q8$KAEpJ&B{%w>_#0P}Cze7h{s?xTCz2~U)KZY)6m|O5YTs;-(;2E- zM;l8>`Tw)!p9Wz8Qfy*gB<1yXt|DkR2kkt;4I3bpA~Vcs(dge_z#+^>5 z?`_bJ;Cw;g`uIcHmrpb+{?2i<(QafkBD!8R$#qxmh>yAE*1T_CY`+1XB5VM#YFzSH ziB;gnChohOaEhBgs1PzzAh`G$jor*>H`j935_;S%Uyu0sO)#kPQvNWg zVZl-;zM?y3{NVWbj~A~Mp8C}y7TaPqk6_g44(_Yva3i65Cxq4aRsZ1gYpjazdc^)mT;4cvER82+=9|0f;>?vo@Z@7?@?XJ1Lx`v+617Diy+ zjMH^b)GbV4Jbo*;*baY<=_Qaqf2P1&vv@qw;eGk-huQ1U?brRm694R~(r||7I2yxT zu!*lMaRedyl|=mQl~fn#7GU~K=}dD#SRn<2 zlTMLbYAI>QIF)9ioVT;DqdK81nhfvjyF}BD=ttD*nKVE&CF5IL+T2M6?V)*~rhK0# zHK&P0!4A#yFv?nuMBKP`82t{84BD_u8V*&wD@fw@=z}lnN z-$$O;ztvKNP!H89oF7AJLiYP#NP%sa7LdY^o^Sr|7vfyRmiAiR=>H)EVgq7+ij6HW z!31HKc&rNC@q%PbH;)hstpG~u#Ef(sQ1L?`<8iOy6zZdP5QMFz3yVsYhb^A5fjo6nY(Lmt=?UIp)(5-}^tU{YTM)C48eiiss6Gp8U(j z_wwX2)0y%nix6uZC4Kx{3s)$iLhTE{@$UmZ2mDNsWzKU1SuffliHrIWkiUV$qC zlHlJg(9=YlxjF^7Dh03$hmyxD20m+h)AWw~MUL$ia6`#{=5NG|a*EWR==~l zaP=!dXr;4clzIRiw@ivl6)%Ol|r z-TVZ~x?{Sp5~*NHsXu)<`9_2yW$-V&|0gzhu-@=2-4W-5Fp88aiNDuvPXb`wH(5k; zI$eTE?oK~mYiSLpL`3`x&9nCawY+gFzyY5HRrRTDekbbiWsuGD7adtZ@;o1`(b9tb zM5vNq^bW8`P-1^$RY-=&a>@8eHTM$FlAeK;!(w66u)bMKB9(+IFvx$~cujoAMlvCv zK&3#wYHwi;mjky~WR5l0xsx%Hy&$pKVo8uClCT1fQgl@@599M5Vh)92Dpb`vY^;WG z=H#o5myHrgTRA7ul~KauRfiQK01;=kz_Q0Ta!Vo!`jlzs6Q>U~o>`PEef88H<^RMT zL-%v~TK8CY{;_1T#2$V|yS$^d1QUe(ntlhr+E-0^MP9~B_i2D4ir1-ZoGbiWnKlUp zX^EG__rOi+SND5NXAS8#&wG5@S%7{8s9 z?80C^5ko0dfLJtFBCn`f6q0WWcHv*nol|&XB|p>UM&b9ZC#&P-xp!{(_a18pDn(r} z(wbg!edhZ%TC!Gx<-^YVX}dGdcTVqnO09f0bF()mBCYcT0F3oEhL2Oo7WVy|0(Ht& z9P5I|E-4;=*EQoqx`;KQ?bf@hS+e+=rC+$@ua6V*e}(1>THgI=id(vKVwHh3s}>e-7cic>OHh$*+G`<|@w1yi=UC^=s;M zjsMP%oEp^vGCA3^^`S>znMWW_l8qBDRf7HI2!~O|_I2Jd+_fijBwb zPesZCWWE4`0U@L%_C-i|b;`q9L774CK=>``B`OiG8@H?czl05~H_84um^OFXdK4>s zIg+<81T@bwaWW&y91n=d`^wujVEf2$=l-47eFdZo?iS=RaQUI7>I=Cw2P$obcAJEk zGkppiG>k(2BiJ7vz18hCiotk4(sFZ%>WRIL0&t=_Ffk6Nt8n=<*Rta8aNQ1_ut9WI zAB7`dLm^I+QM*d=j1UEza|=JQpEFwN8T7Z>=9}GtB=0DP{<@v`(w& z0d~n9rG`YwlL56qJLWOMi;ZFE|FE!oi>9AhB5z9OwS+IonXSqAN~8oKI{-|~+jV82 z&KLHJwy(Oxf@7L1YkQoDOd3ZH-ik``gq*)vc3^v+L#Rmo6UqfuQXTaYp3%^y+rX=K zPPsseoA}gO5F>1C00H%lCkndbZe()3VeN}0Hh_v{741}xD3Bbym&*flfwNNvCJwtF z`-5*$&PV?pe)@jZm+Cki&46*O4ifYx2%a9=I({Wfab)whVB z`Wi}t5Dl{{Ab+ixk^?Ju3EG>yx@-JZxbAceubO=$Jc;pne{D zI~nwLiK`5#PG{H)*fM$VWoprtXn+L?XNn`x%>tanYyAlY5pjL;oXCD{5(K;oBT5Eg zkDS=52}QW`MU;HD zw#948>e9euf!6KOhZRsDZu?3&U0&%~+drP( zA)G%UVG-gE9>vJ{x$-$y$Q5Md6?#SZH6 zHKI~iRI=!vm~Ov+(8Q^-+;*m5pZEUKTATOP%B5Y_$v>>j9kk2)%q73xeQlDZsT}9V zPkt%U8V9!%UY-*%d{jbFw8UWEC>7T3BEC1@LQ}aBN6uGOzPnILF;qRW<3L0B7V`gE)f@6INkEJmjsbJubzp)V$@-osR5YF}msPhCyA{ZWGX6}KPqGV;m@ zwa>|lHyh<-D|{A7p`~Sei8Ja*t{@VnL|1iB<25O^VWJgq_7>tLvS zwBePFA2@j2MOfq}h7Gn(9IPwU8scL}AZY*k$^PJsEdU1Ot8m0f~i zd~=O-M-b6!Ek;Q7bKX~09L9u|3Yks+WG^*;Y?O}fLr?{8tDPTFQTujJCchT)m|1|# z1XjjpU=yW5zQ#B!Q*lGU|D)Eu#M2X1-kxd`+>bfCUZef4cr8r`oW1Gevk&2`L4Qz>-rdVRb^950vBPT!Xx( z!sv@~Mb_4_$m631BkH66f(`85;G8v&-R;QNvHPkWygCIzE5AVlbOo3?V0x1iKy4Jl zRUcI>P_!uOvy64~hjtFP>LKK3*UHq}CyMEoKDsT-;6LQ=iumuh`Gs^3TNixTj^7Q| zmW%aXs)){`(>~r`ycnk9(DC}i($_DSB$mt^uNdy7*(y-eqhuerdc#+6x`iq0HG!dN zkCpg2|G+c=aE?%%b#e)1)a+&sRR+*UJuWAwh}K%FazlT*C$K|sU~%Jfpi+x}?EILLoO zpau%}9lhWsQ1NgcZLx?9SR=SqH^8gB99ky0Yue?fzW)$Dv|OrP<-Ow3GQJhuYvSA{ z_^!5x#eVOx4F1wk{xm>MkdUz9={sF?* zYk*r6t(VZL?*)zy9PwlD_nHrFGxJw3^WBwfi;KTwb*5Zt!lrR}z+=?D$FNKQAunQG zI5nT!(il=;m$e;Fzwz2UDod-KYcM|~3C;n(kP}_~d_IJM+V9KUM9)uo1fFHJvx@ z1zGOK5Bv{6Q%hq|4`HXcOaNVPn?W2z8MwJCGCgRG~Fht~Y>jDki6)4n)9 zZI3HMERSei%D(z-r$6ZO#{19?eXR-qenlHA3Q~LgbbzHKdZJqc5X8scQo_zA>*(Hu@xfUCyBHkVU9C z+}Lkl_TV7Tz4)Lf)Z(p8oz1yWWxv<8rKM2tNkJCc2+T6(0e8ZjiiPXB#e^2QH)&17 z(tWvpWnorb3iaBw7ita~S*J6YoV^zDal$TVavfIbdxw5n^d6Kmy1obXaEVH3>5wV* z8gkE~9PL2rkWdmi0wiIKqXV5=e51KyyFU#hl`6Tum-0rO8e$M`>3C!(%OL+K{(2@e z+;n}Kx#^Y7*C!Ek74b?|X)ctuyb%LmxH@nBV0)36LGCtvJBPt4_VS2M1&?QvqE4NR zvhcH@&FM$bJ2RkUNlu>;kc?Uyr_lWq`LsKdTD&Sf@`tdg8D3gBengG`RV`=qOx5pE z8gQ7o*>xr#)|~~j)L(x@eKNP3-(>Mv`F5bb45mjF_}!C4i`9wGwsJv|*a#po>c%_LjqOWpBQVwygIPsFXy z>2C{C1aq~-;d`73w!dB4Yn%5aS6o99g@=S)=R*8pS!iWiVNJ|jB2B0c=`Ruzgd+xh zRX=LJxYjvT>rcxqg&@OUY+X1kK4#g4tD_Ywq9$6xdw=-c&H$^2xX_2dwVP2cuPz%b zw9Ma(QdDJ_sy=I&$u#l0Qa=LUznky8r1R8-mjCNHMaf<6EpT+*{ZdUy+{N`Wh{@vq zvwgxQ(`VIp&$J_c3$KY#)r+Zmd*6YdmPCmPP&`sT>jSUIs*Y9ESnoWvWSd%%YG}7z z#RWFov|``9eJdnMB4#=9Cf(?EJ(?GTwnu9uk~7iTNBxXdJ8xXyzLVbB=-%=sLsH|n z-lk@2mTnw+K_he ze=aa$Szusq_Dd^5%EONx(kAvaEbIxd%PFLa+8y3{s*2=-%b(l&v}T5sX1#>G_^C>2 zo_FF`zU8tXGsSCz2z(}~_vxXY?@SiAgZAE$?v-pDrWVUDHMhd$@f3n2IX#YeaweJu zqwa|{ml5q&PZLksi6$b&sDUMEP~pX)1roE_4u?U!it?3Ag-W$(FkdQ=VRuyjBW`^IK7Iy*bk(5U~M~o zcUepY@W^ve)?j06I$sZ2sV~)2rYEK^HOfT{9eAAI+9jmF^BJ}8GyJhsyJK|VH1lQa zvTg7Ac@digmEta%&^zB>>1mw!gSvmbAP) zHUq8ev9zlt6i#Nd>kEj7K^5Dc*(PVZim6vFu^po=F1IP&&PGbYmF-Mxp{q3V2D!n|^UtwnU#UpI;! zo>UO+t1{rS5g-ggoorTu1DUvq69btHS5NC~MIm;A0(N=BMJv$R$d&Htw;_eu)GVk^ z(Y88&)YL4NFrz9LcH;a0Z1Mtu))!m}IBf#^J=Q~Rm&b1`%osxaJgktd7 zmyg=DbG}*OwKggCE6`<@_rK(J4$D5MC~i({y>MvzMd6DTW384@ zyvM)l(vejDaP@w##r$Xgra8KP7mZbyuQx~A7GsmYmN~YHtkoO6s@LX?8B1(A=Wf^4~4jE_)|BY0o@L`jK@ChA)qL~K&l)Ovdm0pv8i7CK9_UJ-r(SLw1-VJE^fU3>m3!MYalk!k@1+o0 zj2$}Mqd8bRXe{}ul5d$Vw)_FT*JkJ`X5D6JQp%=-zj0+GLetz0xiuhXEU)(Q2_E0n zHjqt9!a<`~VSjvnVN>mj-?&O#rEYO1U~4wN$a^uLNO{bCmt|^$G~e93$pdOVyZykc zzW@)7=*TlIAnd{kX;d5Ny&wBwx!a!;$}GRMMaK#A8DBjEj>LME?9X&TW-*X+!Y1TN zU2{$X7a5I#(H&pwui2q-w|qKMA5-)l1ZkST&gl%l=UCD=Y~MWc=5)N>$@-4qxcT=) z7mpALu+>Nv+=vxRVw{}}tLt0K)d>lHt4d`uDDRQjvJ3ux$a0gF2W}pt%Z)$QMP9cW z9;4&>ti397?%oLGv9hRue3nCX?NG!Q9M-0BYwxahJL_om$$8n;M84=el{R#&wbjNBWEn z2o$o93vAG$h*U>cK3Sc#9%9=}nkji=Ly@rG@-a>2G*`-#!Zf=_`6;_Cs3i z@G*Nsbi1e0xB5YwDAf)JrMu{hNqQOZ)e>nZcp=3K72EWQ^nJz^>9M!W6E?X>BU+dr z?1xUi@50#v_oba387I<}2{1X6+}^n{FuE=_)?l@)L45+1w-|?L#?*6pTc3%7QR$tQ?-2BU_$%gC z@(B|Y-KAm`n~izQ*Ia6rw9c!W8+SXjyQgZ6mSnvofhiRxY_=&*^p2eX_1ZRUam>V4 zQUcJnV>~7hADEo>N@?NU@dXug3#dA$#W= z#HcI|K|l}lXq(c7ltPtvz%l^t**if}tO($O0Q43%4@ou! zZ{kwD8?WA5d-4jU~5Y2$V~p;_&BV@DCtY1F(%R zZ5h@w0J8;5PX;5n8t2G{^gYaInBX7>KdU*TTe!;EQ!b8SNmi} z5zUIgp1T=Ri)XByAkKDDNY0gtIYSHE%CpW5#ry+B$Lz<^~47MLfQhIwCM$Ta}A z>jS8ASU~^iHVP?a(Kirio z%k>tXqJOXRw$5I<#%(z(OL$Fap6dM09WD0?99yDk_nzDcB!BmuzbUN)@lh*)xu}Z{ zdT$S5Sd&)GY>1Q(>P?&a?H_fqa~kUs5Ei|K_==h2{$$6$y52Um;f|tz<3jFarqVL( z0*+8I(J%r8zNLCVzr@FU@lcm}OWDS^%*pF| z_z<-90}H&97PcR7G9ZkEmH2h)V6I+huJ$P{C}Ko_`bk*}g1PjtUxRmc8CQ$kV`qKR z(SD(qmX)%AZ0dMG*d%48I4sF<(arIHn!*Nxa3WMY`P(aCyX z3pZz|B zo}~Z`)CvXui3~J24H#(GXXzzSoeg34A;tv1e+;^Suw{i_cr+sj>b%^mI0G>UbRfkk`8?-x926h5 zm~3nct2+k0Y!HeinvF`k5Qz^B+{I3OW!_B=LT?XP=|l|g{iNh*^>VrUwAIi13KTO| zR(58YPK}|_^mbVUJb|#~!T#~DdjCyW3pBF`Cy-F`bNB^IoF#OUv8Mj12K}*^YKYk- z|0f^pxMoVi_H5=!sycp>dI5Oz|9Uwg8xae1w$@t9Q~bl04g1wE_iBbyXo;-{%ugiqM8 zuVBXV2z62c1+cbbBZf$Y{mo|+nWIk^iGH3}K#JX4VA3RdzHDGmPRaX|u^ew(zMQEU zU{1PIj?W8LxV4gkk6MI2PvQT$eU}V>f#8=A7%ERfclK?{CylXdV^@8X9T%GVump@R zOY0JZIMtNr0QO?8!Ca6@bCw<}8^B6Z@<+yuyzxCDe`>Vr^8?C63VSIMXxU|>Km<=p2?t^DM4&?bmrBmzcX&JnQ1Q&HWFw?MMuh9W2fPj7*yaC}6IC8q z=udVa#rB2vC=@A%?ChZXa9&eS?}!3uuxSat)E<(Bx~x<;K54jk6rOmc$alqhVF)PLMP}3oLySdJv z-0b9Dbu&Y^8 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.Attribute) Profile { + var attributeSlice []*yotiprotoattr.Attribute + attributeSlice = append(attributeSlice, attr) + + return Profile{ + attributeSlice: attributeSlice, + } +} + +func AssertServerCertSerialNo(t *testing.T, expectedSerialNo string, actualSerialNo *big.Int) { + expectedSerialNoBigInt := new(big.Int) + expectedSerialNoBigInt, ok := expectedSerialNoBigInt.SetString(expectedSerialNo, 10) + if !ok { + t.Error("Unexpected error when setting string as big int") + } + + if expectedSerialNoBigInt.Cmp(actualSerialNo) != 0 { //0 == equivalent + t.Errorf( + "Parsed anchor OriginServerCerts is incorrect. Expected: %q, actual: %q", + expectedSerialNo, + actualSerialNo) + } +} + +func CreateAnchorSliceFromTestFile(t *testing.T, filename string) []*yotiprotoattr.Anchor { + anchorBytes, err := DecodeTestFile(t, filename) + + if err != nil { + t.Errorf("error decoding test file: %q", err) + } + + protoAnchor := &yotiprotoattr.Anchor{} + if err := proto.Unmarshal(anchorBytes, protoAnchor); err != nil { + t.Errorf("Error converting test anchor bytes into a Protobuf anchor. Error: %q", err) + } + + protoAnchors := append([]*yotiprotoattr.Anchor{}, protoAnchor) + + return protoAnchors +} + +func DecodeTestFile(t *testing.T, filename string) (result []byte, err error) { + base64Bytes := readTestFile(t, filename) + base64String := string(base64Bytes) + anchorBytes, err := base64.StdEncoding.DecodeString(base64String) + if err != nil { + return nil, err + } + return anchorBytes, nil +} + +func readTestFile(t *testing.T, filename string) (result []byte) { + b, err := ioutil.ReadFile(filename) + if err != nil { + t.Error(err) + } + + return b +} + +func CreateHeaders() (result map[string]string) { + + headers := make(map[string]string) + + headers["Header1"] = "test" + + return headers +} + +func createStandardAmlProfile() (result AmlProfile) { + var amlAddress = AmlAddress{ + Country: "GBR"} + + var amlProfile = AmlProfile{ + GivenNames: "Edward Richard George", + FamilyName: "Heath", + Address: amlAddress} + + return amlProfile +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiattributevalue.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiattributevalue.go new file mode 100644 index 0000000..9b754b5 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiattributevalue.go @@ -0,0 +1,60 @@ +package yoti + +import "log" + +// Deprecated: AttributeType format of the attribute +type AttributeType int + +const ( + // AttributeTypeDate date format + AttributeTypeDate AttributeType = 1 + iota + // AttributeTypeText text format + AttributeTypeText + // AttributeTypeJPEG JPEG format + AttributeTypeJPEG + // AttributeTypePNG PNG fornmat + AttributeTypePNG + // AttributeTypeJSON JSON fornmat + AttributeTypeJSON +) + +// Deprecated: Will be removed in v3.0.0, values here will be available on Attribute objects. AttributeValue represents a small piece of information about a Yoti user such as a photo of the user or the +// user's date of birth. +type AttributeValue struct { + // Type represents the format of the piece of user data, whether it is a date, a piece of text or a picture + // + // Note the potential values for this variable are stored in constants with names beginning with + // 'AttributeType'. These include: + // yoti.AttributeTypeDate + // yoti.AttributeTypeText + // yoti.AttributeTypeJPEG + // yoti.AttributeTypePNG + // yoti.AttributeTypeJSON + Type AttributeType + Value []byte +} + +// Deprecated: Will be removed in v3.0.0, use GetMIMEType() instead. GetContentType returns the MIME type of this piece of Yoti user information. For more information see: +// https://en.wikipedia.org/wiki/Media_type +func (val AttributeValue) GetContentType() (result string) { + switch val.Type { + case AttributeTypeDate: + result = "text/plain; charset=UTF-8" + + case AttributeTypeText: + result = "text/plain; charset=UTF-8" + + case AttributeTypeJPEG: + result = "image/jpeg" + + case AttributeTypePNG: + result = "image/png" + + case AttributeTypeJSON: + result = "application/json; charset=UTF-8" + + default: + log.Printf("Unable to find a matching MIME type for value type %q", val.Type) + } + return +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yoticlient.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yoticlient.go new file mode 100644 index 0000000..429eb33 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yoticlient.go @@ -0,0 +1,476 @@ +package yoti + +import ( + "crypto/rsa" + "encoding/json" + "errors" + "fmt" + "log" + "net/http" + "reflect" + "strconv" + "strings" + "time" + + "github.com/getyoti/yoti-go-sdk/attribute" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" + "github.com/getyoti/yoti-go-sdk/yotiprotocom" + "github.com/golang/protobuf/proto" +) + +const ( + apiURL = "https://api.yoti.com/api/v1" + sdkIdentifier = "Go" + sdkVersionIdentifier = "2.3.1" + + authKeyHeader = "X-Yoti-Auth-Key" + authDigestHeader = "X-Yoti-Auth-Digest" + sdkIdentifierHeader = "X-Yoti-SDK" + sdkVersionIdentifierHeader = sdkIdentifierHeader + "-Version" + attributeAgeOver = "age_over:" + attributeAgeUnder = "age_under:" +) + +// Client represents a client that can communicate with yoti and return information about Yoti users. +type Client struct { + // SdkID represents the SDK ID and NOT the App ID. This can be found in the integration section of your + // application dashboard at https://www.yoti.com/dashboard/ + SdkID string + + // Key should be the security key given to you by yoti (see: security keys section of + // https://www.yoti.com/dashboard/) for more information about how to load your key from a file see: + // https://github.com/getyoti/yoti-go-sdk/blob/master/README.md + Key []byte +} + +// 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) { + var errStrings []string + userProfile, _, errStrings = getActivityDetails(doRequest, token, client.SdkID, client.Key) + if len(errStrings) > 0 { + firstError = errors.New(errStrings[0]) + } + return userProfile, firstError +} + +// GetActivityDetails 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) GetActivityDetails(token string) (ActivityDetails, []string) { + _, activityDetails, errStrings := getActivityDetails(doRequest, token, client.SdkID, client.Key) + return activityDetails, errStrings +} + +func getActivityDetails(requester httpRequester, encryptedToken, sdkID string, keyBytes []byte) (userProfile UserProfile, activityDetails ActivityDetails, errStrings []string) { + var err error + var key *rsa.PrivateKey + var httpMethod = HTTPMethodGet + + if key, err = loadRsaKey(keyBytes); err != nil { + errStrings = append(errStrings, fmt.Sprintf("Invalid Key: %s", err.Error())) + return + } + + // query parameters + var token string + if token, err = decryptToken(encryptedToken, key); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + + var nonce string + if nonce, err = generateNonce(); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + + timestamp := getTimestamp() + + // create http endpoint + endpoint := getProfileEndpoint(token, nonce, timestamp, sdkID) + + var headers map[string]string + if headers, err = createHeaders(key, httpMethod, endpoint, nil); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + + var response *httpResponse + if response, err = requester(apiURL+endpoint, headers, httpMethod, nil); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + + if response.Success { + var parsedResponse = profileDO{} + + if err = json.Unmarshal([]byte(response.Content), &parsedResponse); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + + if parsedResponse.Receipt.SharingOutcome != "SUCCESS" { + err = ErrSharingFailure + errStrings = append(errStrings, err.Error()) + } else { + var attributeList *yotiprotoattr.AttributeList + if attributeList, err = decryptCurrentUserReceipt(&parsedResponse.Receipt, key); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + id := parsedResponse.Receipt.RememberMeID + + userProfile = addAttributesToUserProfile(id, attributeList) //deprecated: will be removed in v3.0.0 + + profile := Profile{ + attributeSlice: createAttributeSlice(attributeList), + } + + var formattedAddress string + formattedAddress, err = ensureAddressProfile(profile) + if err != nil { + log.Printf("Unable to get 'Formatted Address' from 'Structured Postal Address'. Error: %q", err) + } else if formattedAddress != "" { + if _, err = profile.StructuredPostalAddress(); err != nil { + errStrings = append(errStrings, err.Error()) + return + } + + protoStructuredPostalAddress := getProtobufAttribute(profile, attrConstStructuredPostalAddress) + + addressAttribute := &yotiprotoattr.Attribute{ + Name: attrConstAddress, + Value: []byte(formattedAddress), + ContentType: yotiprotoattr.ContentType_STRING, + Anchors: protoStructuredPostalAddress.Anchors, + } + + profile.attributeSlice = append(profile.attributeSlice, addressAttribute) + } + + activityDetails = ActivityDetails{ + UserProfile: profile, + rememberMeID: id, + } + } + } else { + switch response.StatusCode { + case http.StatusNotFound: + err = ErrProfileNotFound + default: + err = ErrFailure + } + } + + if err != nil { + errStrings = append(errStrings, err.Error()) + } + + return userProfile, activityDetails, errStrings +} + +func getProtobufAttribute(profile Profile, key string) *yotiprotoattr.Attribute { + for _, v := range profile.attributeSlice { + if v.Name == attrConstStructuredPostalAddress { + return v + } + } + + return nil +} + +func addAttributesToUserProfile(id string, attributeList *yotiprotoattr.AttributeList) (result UserProfile) { + result = UserProfile{ + ID: id, + OtherAttributes: make(map[string]AttributeValue)} + + if attributeList == nil { + return + } + + for _, a := range attributeList.Attributes { + switch a.Name { + case "selfie": + + switch a.ContentType { + case yotiprotoattr.ContentType_JPEG: + result.Selfie = &Image{ + Type: ImageTypeJpeg, + Data: a.Value} + case yotiprotoattr.ContentType_PNG: + result.Selfie = &Image{ + Type: ImageTypePng, + Data: a.Value} + } + case "given_names": + result.GivenNames = string(a.Value) + case "family_name": + result.FamilyName = string(a.Value) + case "full_name": + result.FullName = string(a.Value) + case "phone_number": + result.MobileNumber = string(a.Value) + case "email_address": + result.EmailAddress = string(a.Value) + case "date_of_birth": + parsedTime, err := time.Parse("2006-01-02", string(a.Value)) + if err == nil { + result.DateOfBirth = &parsedTime + } else { + log.Printf("Unable to parse `date_of_birth` value: %q. Error: %q", a.Value, err) + } + case "postal_address": + result.Address = string(a.Value) + case "structured_postal_address": + structuredPostalAddress, err := attribute.UnmarshallJSON(a.Value) + + if err == nil { + result.StructuredPostalAddress = structuredPostalAddress + } else { + log.Printf("Unable to parse `structured_postal_address` value: %q. Error: %q", a.Value, err) + } + case "gender": + result.Gender = string(a.Value) + case "nationality": + result.Nationality = string(a.Value) + default: + if strings.HasPrefix(a.Name, attributeAgeOver) || + strings.HasPrefix(a.Name, attributeAgeUnder) { + + isAgeVerified, err := parseIsAgeVerifiedValue(a.Value) + + if err == nil { + result.IsAgeVerified = isAgeVerified + } else { + log.Printf("Unable to parse `IsAgeVerified` value: %q. Error: %q", a.Value, err) + } + } + + switch a.ContentType { + case yotiprotoattr.ContentType_DATE: + result.OtherAttributes[a.Name] = AttributeValue{ + Type: AttributeTypeDate, + Value: a.Value} + case yotiprotoattr.ContentType_STRING: + result.OtherAttributes[a.Name] = AttributeValue{ + Type: AttributeTypeText, + Value: a.Value} + case yotiprotoattr.ContentType_JPEG: + result.OtherAttributes[a.Name] = AttributeValue{ + Type: AttributeTypeJPEG, + Value: a.Value} + case yotiprotoattr.ContentType_PNG: + result.OtherAttributes[a.Name] = AttributeValue{ + Type: AttributeTypePNG, + Value: a.Value} + case yotiprotoattr.ContentType_JSON: + result.OtherAttributes[a.Name] = AttributeValue{ + Type: AttributeTypeJSON, + Value: a.Value} + } + } + } + formattedAddress, err := ensureAddressUserProfile(result) + if err != nil { + log.Printf("Unable to get 'Formatted Address' from 'Structured Postal Address'. Error: %q", err) + } else if formattedAddress != "" { + result.Address = formattedAddress + } + + return +} + +func createAttributeSlice(protoAttributeList *yotiprotoattr.AttributeList) (result []*yotiprotoattr.Attribute) { + if protoAttributeList != nil { + result = append(result, protoAttributeList.Attributes...) + } + + return result +} + +func ensureAddressUserProfile(result UserProfile) (address string, err error) { + if result.Address == "" && result.StructuredPostalAddress != nil { + var formattedAddress string + formattedAddress, err = retrieveFormattedAddressFromStructuredPostalAddress(result.StructuredPostalAddress) + if err == nil { + return formattedAddress, nil + } + } + + return "", err +} + +func ensureAddressProfile(profile Profile) (address string, err error) { + if profile.Address() == nil { + var structuredPostalAddress *attribute.JSONAttribute + if structuredPostalAddress, err = profile.StructuredPostalAddress(); err == nil { + if (structuredPostalAddress != nil && !reflect.DeepEqual(structuredPostalAddress, attribute.JSONAttribute{})) { + var formattedAddress string + formattedAddress, err = retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress.Value()) + if err == nil { + return formattedAddress, nil + } + } + } + } + + return "", err +} + +func retrieveFormattedAddressFromStructuredPostalAddress(structuredPostalAddress interface{}) (address string, err error) { + parsedStructuredAddressMap := structuredPostalAddress.(map[string]interface{}) + if formattedAddress, ok := parsedStructuredAddressMap["formatted_address"]; ok { + return formattedAddress.(string), nil + } + return +} + +func parseIsAgeVerifiedValue(byteValue []byte) (result *bool, err error) { + stringValue := string(byteValue) + + var parseResult bool + parseResult, err = strconv.ParseBool(stringValue) + + if err != nil { + return nil, err + } + + result = &parseResult + + return +} + +func decryptCurrentUserReceipt(receipt *receiptDO, key *rsa.PrivateKey) (result *yotiprotoattr.AttributeList, err error) { + var unwrappedKey []byte + if unwrappedKey, err = unwrapKey(receipt.WrappedReceiptKey, key); err != nil { + return + } + + if receipt.OtherPartyProfileContent == "" { + return + } + + var otherPartyProfileContentBytes []byte + if otherPartyProfileContentBytes, err = base64ToBytes(receipt.OtherPartyProfileContent); err != nil { + return + } + + encryptedData := &yotiprotocom.EncryptedData{} + if err = proto.Unmarshal(otherPartyProfileContentBytes, encryptedData); err != nil { + return nil, err + } + + var decipheredBytes []byte + if decipheredBytes, err = decipherAes(unwrappedKey, encryptedData.Iv, encryptedData.CipherText); err != nil { + return nil, err + } + + attributeList := &yotiprotoattr.AttributeList{} + if err := proto.Unmarshal(decipheredBytes, attributeList); err != nil { + return nil, err + } + + return attributeList, nil +} + +// PerformAmlCheck performs an Anti Money Laundering Check (AML) for a particular user. +// Returns three boolean values: 'OnPEPList', 'OnWatchList' and 'OnFraudList'. +func (client *Client) PerformAmlCheck(amlProfile AmlProfile) (AmlResult, error) { + return performAmlCheck(amlProfile, doRequest, client.SdkID, client.Key) +} + +func performAmlCheck(amlProfile AmlProfile, requester httpRequester, sdkID string, keyBytes []byte) (result AmlResult, err error) { + var key *rsa.PrivateKey + var httpMethod = HTTPMethodPost + + if key, err = loadRsaKey(keyBytes); err != nil { + err = fmt.Errorf("Invalid Key: %s", err.Error()) + return + } + + var nonce string + if nonce, err = generateNonce(); err != nil { + return + } + + timestamp := getTimestamp() + endpoint := getAMLEndpoint(nonce, timestamp, sdkID) + + var content []byte + if content, err = json.Marshal(amlProfile); err != nil { + return + } + + var headers map[string]string + if headers, err = createHeaders(key, httpMethod, endpoint, content); err != nil { + return + } + + var response *httpResponse + if response, err = requester(apiURL+endpoint, headers, httpMethod, content); err != nil { + return + } + + if response.Success { + result, err = GetAmlResult([]byte(response.Content)) + return + } + + err = fmt.Errorf( + "AML Check was unsuccessful, status code: '%d', content:'%s'", response.StatusCode, response.Content) + + return +} + +func getProfileEndpoint(token, nonce, timestamp, sdkID string) string { + return fmt.Sprintf("/profile/%s?nonce=%s×tamp=%s&appId=%s", token, nonce, timestamp, sdkID) +} + +func getAMLEndpoint(nonce, timestamp, sdkID string) string { + return fmt.Sprintf("/aml-check?appId=%s×tamp=%s&nonce=%s", sdkID, timestamp, nonce) +} + +func getAuthDigest(endpoint string, key *rsa.PrivateKey, httpMethod string, content []byte) (result string, err error) { + digest := httpMethod + "&" + endpoint + + if content != nil { + digest += "&" + bytesToBase64(content) + } + + digestBytes := utfToBytes(digest) + var signedDigestBytes []byte + + if signedDigestBytes, err = signDigest(digestBytes, key); err != nil { + return + } + + result = bytesToBase64(signedDigestBytes) + return +} + +func getTimestamp() string { + return strconv.FormatInt(time.Now().Unix()*1000, 10) +} + +func createHeaders(key *rsa.PrivateKey, httpMethod string, endpoint string, content []byte) (headers map[string]string, err error) { + var authKey string + if authKey, err = getAuthKey(key); err != nil { + return + } + + var authDigest string + if authDigest, err = getAuthDigest(endpoint, key, httpMethod, content); err != nil { + return + } + + headers = make(map[string]string) + + headers[authKeyHeader] = authKey + headers[authDigestHeader] = authDigest + headers[sdkIdentifierHeader] = sdkIdentifier + headers[sdkVersionIdentifierHeader] = sdkVersionIdentifier + + return headers, err +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprofile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprofile.go new file mode 100644 index 0000000..70f1e1d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprofile.go @@ -0,0 +1,151 @@ +package yoti + +import ( + "github.com/getyoti/yoti-go-sdk/attribute" + "github.com/getyoti/yoti-go-sdk/yotiprotoattr" +) + +const ( + attrConstSelfie = "selfie" + attrConstGivenNames = "given_names" + attrConstFamilyName = "family_name" + attrConstFullName = "full_name" + attrConstMobileNumber = "phone_number" + attrConstEmailAddress = "email_address" + attrConstDateOfBirth = "date_of_birth" + attrConstAddress = "postal_address" + attrConstStructuredPostalAddress = "structured_postal_address" + attrConstGender = "gender" + attrConstNationality = "nationality" +) + +// 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 []*yotiprotoattr.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 { + if a.Name == attrConstSelfie { + attribute, err := attribute.NewImage(a) + + if err == nil { + return attribute + } + } + } + return nil +} + +// 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 { + if a.Name == attrConstGivenNames { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstFamilyName { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstFullName { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstMobileNumber { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstEmailAddress { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstDateOfBirth { + return attribute.NewTime(a) + } + } + return nil, nil +} + +// 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 { + if a.Name == attrConstAddress { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstStructuredPostalAddress { + return attribute.NewJSON(a) + } + } + return nil, nil +} + +// 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 { + if a.Name == attrConstGender { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attrConstNationality { + return attribute.NewString(a) + } + } + return nil +} + +// 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 { + if a.Name == attributeName { + return attribute.NewGeneric(a) + } + } + return nil +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.pb.go new file mode 100644 index 0000000..5287329 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.pb.go @@ -0,0 +1,245 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: Attribute.proto + +package yotiprotoattr + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// ContentType indicates how to interpret the ‘Value’ field of an Attribute. +type ContentType int32 + +const ( + // UNDEFINED should not be seen, and is used as an error placeholder + // value. + ContentType_UNDEFINED ContentType = 0 + // STRING means the value is UTF-8 encoded text. + ContentType_STRING ContentType = 1 + // JPEG indicates a standard .jpeg image. + ContentType_JPEG ContentType = 2 + // Date as string in RFC3339 format (YYYY-MM-DD). + ContentType_DATE ContentType = 3 + // PNG indicates a standard .png image. + ContentType_PNG ContentType = 4 + // JSON means the value is encoded using JSON. + ContentType_JSON ContentType = 5 +) + +var ContentType_name = map[int32]string{ + 0: "UNDEFINED", + 1: "STRING", + 2: "JPEG", + 3: "DATE", + 4: "PNG", + 5: "JSON", +} + +var ContentType_value = map[string]int32{ + "UNDEFINED": 0, + "STRING": 1, + "JPEG": 2, + "DATE": 3, + "PNG": 4, + "JSON": 5, +} + +func (x ContentType) String() string { + return proto.EnumName(ContentType_name, int32(x)) +} + +func (ContentType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_44d6ebe2a990cc1d, []int{0} +} + +type Attribute struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + ContentType ContentType `protobuf:"varint,3,opt,name=content_type,json=contentType,proto3,enum=attrpubapi_v1.ContentType" json:"content_type,omitempty"` + Anchors []*Anchor `protobuf:"bytes,4,rep,name=anchors,proto3" json:"anchors,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Attribute) Reset() { *m = Attribute{} } +func (m *Attribute) String() string { return proto.CompactTextString(m) } +func (*Attribute) ProtoMessage() {} +func (*Attribute) Descriptor() ([]byte, []int) { + return fileDescriptor_44d6ebe2a990cc1d, []int{0} +} + +func (m *Attribute) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Attribute.Unmarshal(m, b) +} +func (m *Attribute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Attribute.Marshal(b, m, deterministic) +} +func (m *Attribute) XXX_Merge(src proto.Message) { + xxx_messageInfo_Attribute.Merge(m, src) +} +func (m *Attribute) XXX_Size() int { + return xxx_messageInfo_Attribute.Size(m) +} +func (m *Attribute) XXX_DiscardUnknown() { + xxx_messageInfo_Attribute.DiscardUnknown(m) +} + +var xxx_messageInfo_Attribute proto.InternalMessageInfo + +func (m *Attribute) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Attribute) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *Attribute) GetContentType() ContentType { + if m != nil { + return m.ContentType + } + return ContentType_UNDEFINED +} + +func (m *Attribute) GetAnchors() []*Anchor { + if m != nil { + return m.Anchors + } + return nil +} + +type Anchor struct { + ArtifactLink []byte `protobuf:"bytes,1,opt,name=artifact_link,json=artifactLink,proto3" json:"artifact_link,omitempty"` + OriginServerCerts [][]byte `protobuf:"bytes,2,rep,name=origin_server_certs,json=originServerCerts,proto3" json:"origin_server_certs,omitempty"` + ArtifactSignature []byte `protobuf:"bytes,3,opt,name=artifact_signature,json=artifactSignature,proto3" json:"artifact_signature,omitempty"` + SubType string `protobuf:"bytes,4,opt,name=sub_type,json=subType,proto3" json:"sub_type,omitempty"` + Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` + SignedTimeStamp []byte `protobuf:"bytes,6,opt,name=signed_time_stamp,json=signedTimeStamp,proto3" json:"signed_time_stamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Anchor) Reset() { *m = Anchor{} } +func (m *Anchor) String() string { return proto.CompactTextString(m) } +func (*Anchor) ProtoMessage() {} +func (*Anchor) Descriptor() ([]byte, []int) { + return fileDescriptor_44d6ebe2a990cc1d, []int{1} +} + +func (m *Anchor) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Anchor.Unmarshal(m, b) +} +func (m *Anchor) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Anchor.Marshal(b, m, deterministic) +} +func (m *Anchor) XXX_Merge(src proto.Message) { + xxx_messageInfo_Anchor.Merge(m, src) +} +func (m *Anchor) XXX_Size() int { + return xxx_messageInfo_Anchor.Size(m) +} +func (m *Anchor) XXX_DiscardUnknown() { + xxx_messageInfo_Anchor.DiscardUnknown(m) +} + +var xxx_messageInfo_Anchor proto.InternalMessageInfo + +func (m *Anchor) GetArtifactLink() []byte { + if m != nil { + return m.ArtifactLink + } + return nil +} + +func (m *Anchor) GetOriginServerCerts() [][]byte { + if m != nil { + return m.OriginServerCerts + } + return nil +} + +func (m *Anchor) GetArtifactSignature() []byte { + if m != nil { + return m.ArtifactSignature + } + return nil +} + +func (m *Anchor) GetSubType() string { + if m != nil { + return m.SubType + } + return "" +} + +func (m *Anchor) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *Anchor) GetSignedTimeStamp() []byte { + if m != nil { + return m.SignedTimeStamp + } + return nil +} + +func init() { + proto.RegisterEnum("attrpubapi_v1.ContentType", ContentType_name, ContentType_value) + proto.RegisterType((*Attribute)(nil), "attrpubapi_v1.Attribute") + proto.RegisterType((*Anchor)(nil), "attrpubapi_v1.Anchor") +} + +func init() { proto.RegisterFile("Attribute.proto", fileDescriptor_44d6ebe2a990cc1d) } + +var fileDescriptor_44d6ebe2a990cc1d = []byte{ + // 400 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x92, 0xcf, 0x6e, 0xd3, 0x40, + 0x10, 0xc6, 0x71, 0xec, 0x24, 0xcd, 0xc4, 0xa1, 0xce, 0xf0, 0x47, 0x06, 0x71, 0xb0, 0xca, 0xc5, + 0xaa, 0x84, 0x11, 0xe9, 0x99, 0x43, 0xda, 0x84, 0xa8, 0x08, 0xb9, 0xd1, 0x3a, 0x5c, 0xb8, 0x58, + 0x6b, 0xb3, 0x94, 0x55, 0xeb, 0xb5, 0xb5, 0x1e, 0x47, 0xca, 0x03, 0xf1, 0x80, 0xbc, 0x01, 0xf2, + 0x5a, 0x6e, 0x68, 0x6f, 0x33, 0xdf, 0xef, 0xdb, 0x4f, 0x3b, 0xa3, 0x81, 0xd3, 0x25, 0x91, 0x96, + 0x59, 0x43, 0x22, 0xaa, 0x74, 0x49, 0x25, 0xce, 0x38, 0x91, 0xae, 0x9a, 0x8c, 0x57, 0x32, 0xdd, + 0x7f, 0x3a, 0xfb, 0x63, 0xc1, 0xe4, 0xc1, 0x82, 0x08, 0x8e, 0xe2, 0x85, 0xf0, 0xad, 0xc0, 0x0a, + 0x27, 0xcc, 0xd4, 0xf8, 0x12, 0x86, 0x7b, 0x7e, 0xdf, 0x08, 0x7f, 0x10, 0x58, 0xa1, 0xcb, 0xba, + 0x06, 0x3f, 0x83, 0x9b, 0x97, 0x8a, 0x84, 0xa2, 0x94, 0x0e, 0x95, 0xf0, 0xed, 0xc0, 0x0a, 0x9f, + 0x2f, 0xde, 0x46, 0x8f, 0xd2, 0xa3, 0xab, 0xce, 0xb2, 0x3b, 0x54, 0x82, 0x4d, 0xf3, 0x63, 0x83, + 0x1f, 0x61, 0xcc, 0x55, 0xfe, 0xbb, 0xd4, 0xb5, 0xef, 0x04, 0x76, 0x38, 0x5d, 0xbc, 0x7a, 0xf2, + 0x72, 0x69, 0x28, 0xeb, 0x5d, 0x67, 0x7f, 0x2d, 0x18, 0x75, 0x1a, 0xbe, 0x87, 0x19, 0xd7, 0x24, + 0x7f, 0xf1, 0x9c, 0xd2, 0x7b, 0xa9, 0xee, 0xcc, 0x6f, 0x5d, 0xe6, 0xf6, 0xe2, 0x37, 0xa9, 0xee, + 0x30, 0x82, 0x17, 0xa5, 0x96, 0xb7, 0x52, 0xa5, 0xb5, 0xd0, 0x7b, 0xa1, 0xd3, 0x5c, 0x68, 0xaa, + 0xfd, 0x41, 0x60, 0x87, 0x2e, 0x9b, 0x77, 0x28, 0x31, 0xe4, 0xaa, 0x05, 0xf8, 0x01, 0xf0, 0x21, + 0xb4, 0x96, 0xb7, 0x8a, 0x53, 0xa3, 0xbb, 0xa9, 0x5c, 0x36, 0xef, 0x49, 0xd2, 0x03, 0x7c, 0x03, + 0x27, 0x75, 0x93, 0x75, 0xa3, 0x3b, 0x66, 0x59, 0xe3, 0xba, 0xc9, 0xcc, 0x68, 0xef, 0x60, 0x72, + 0x0c, 0x18, 0x9a, 0x80, 0xa3, 0x80, 0xe7, 0x30, 0x6f, 0x1b, 0xf1, 0x33, 0x25, 0x59, 0x88, 0xb4, + 0x26, 0x5e, 0x54, 0xfe, 0xc8, 0xb8, 0x4e, 0x3b, 0xb0, 0x93, 0x85, 0x48, 0x5a, 0xf9, 0xfc, 0x06, + 0xa6, 0xff, 0x2d, 0x10, 0x67, 0x30, 0xf9, 0x1e, 0xaf, 0xd6, 0x5f, 0xae, 0xe3, 0xf5, 0xca, 0x7b, + 0x86, 0x00, 0xa3, 0x64, 0xc7, 0xae, 0xe3, 0x8d, 0x67, 0xe1, 0x09, 0x38, 0x5f, 0xb7, 0xeb, 0x8d, + 0x37, 0x68, 0xab, 0xd5, 0x72, 0xb7, 0xf6, 0x6c, 0x1c, 0x83, 0xbd, 0x8d, 0x37, 0x9e, 0x63, 0x60, + 0x72, 0x13, 0x7b, 0xc3, 0xcb, 0x05, 0xbc, 0xce, 0xcb, 0x22, 0x3a, 0x94, 0x24, 0x1f, 0xad, 0xfb, + 0xe2, 0xd2, 0xdc, 0xc0, 0xb6, 0x3d, 0x90, 0x1f, 0xb3, 0x16, 0x9b, 0x5b, 0x69, 0x2d, 0xd9, 0xc8, + 0x94, 0x17, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0xbf, 0xae, 0x17, 0x38, 0x49, 0x02, 0x00, 0x00, +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.proto b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.proto new file mode 100644 index 0000000..94179ff --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Attribute.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; + +package attrpubapi_v1; + +option java_package = "com.yoti.api.client.spi.remote.proto"; +option java_outer_classname = "AttrProto"; + +option go_package = "yotiprotoattr"; + + +// ContentType indicates how to interpret the ‘Value’ field of an Attribute. +enum ContentType { + // UNDEFINED should not be seen, and is used as an error placeholder + // value. + UNDEFINED = 0; + + // STRING means the value is UTF-8 encoded text. + STRING = 1; + + // JPEG indicates a standard .jpeg image. + JPEG = 2; + + // Date as string in RFC3339 format (YYYY-MM-DD). + DATE = 3; + + // PNG indicates a standard .png image. + PNG = 4; + + // JSON means the value is encoded using JSON. + JSON = 5; +} + + +message Attribute { + string name = 1; + + bytes value = 2; + + ContentType content_type = 3; + + repeated Anchor anchors = 4; +} + + +message Anchor { + bytes artifact_link = 1; + + repeated bytes origin_server_certs = 2; + + bytes artifact_signature = 3; + + string sub_type = 4; + + bytes signature = 5; + + bytes signed_time_stamp = 6; +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.pb.go new file mode 100644 index 0000000..e75b566 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.pb.go @@ -0,0 +1,174 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: List.proto + +package yotiprotoattr + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// AttributeAndId is a simple container for holding an attribute's value +// alongside its ID. +type AttributeAndId struct { + Attribute *Attribute `protobuf:"bytes,1,opt,name=attribute,proto3" json:"attribute,omitempty"` + AttributeId []byte `protobuf:"bytes,2,opt,name=attribute_id,json=attributeId,proto3" json:"attribute_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AttributeAndId) Reset() { *m = AttributeAndId{} } +func (m *AttributeAndId) String() string { return proto.CompactTextString(m) } +func (*AttributeAndId) ProtoMessage() {} +func (*AttributeAndId) Descriptor() ([]byte, []int) { + return fileDescriptor_e23ec92774814a82, []int{0} +} + +func (m *AttributeAndId) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AttributeAndId.Unmarshal(m, b) +} +func (m *AttributeAndId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AttributeAndId.Marshal(b, m, deterministic) +} +func (m *AttributeAndId) XXX_Merge(src proto.Message) { + xxx_messageInfo_AttributeAndId.Merge(m, src) +} +func (m *AttributeAndId) XXX_Size() int { + return xxx_messageInfo_AttributeAndId.Size(m) +} +func (m *AttributeAndId) XXX_DiscardUnknown() { + xxx_messageInfo_AttributeAndId.DiscardUnknown(m) +} + +var xxx_messageInfo_AttributeAndId proto.InternalMessageInfo + +func (m *AttributeAndId) GetAttribute() *Attribute { + if m != nil { + return m.Attribute + } + return nil +} + +func (m *AttributeAndId) GetAttributeId() []byte { + if m != nil { + return m.AttributeId + } + return nil +} + +type AttributeAndIdList struct { + AttributeAndIdList []*AttributeAndId `protobuf:"bytes,1,rep,name=attribute_and_id_list,json=attributeAndIdList,proto3" json:"attribute_and_id_list,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AttributeAndIdList) Reset() { *m = AttributeAndIdList{} } +func (m *AttributeAndIdList) String() string { return proto.CompactTextString(m) } +func (*AttributeAndIdList) ProtoMessage() {} +func (*AttributeAndIdList) Descriptor() ([]byte, []int) { + return fileDescriptor_e23ec92774814a82, []int{1} +} + +func (m *AttributeAndIdList) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AttributeAndIdList.Unmarshal(m, b) +} +func (m *AttributeAndIdList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AttributeAndIdList.Marshal(b, m, deterministic) +} +func (m *AttributeAndIdList) XXX_Merge(src proto.Message) { + xxx_messageInfo_AttributeAndIdList.Merge(m, src) +} +func (m *AttributeAndIdList) XXX_Size() int { + return xxx_messageInfo_AttributeAndIdList.Size(m) +} +func (m *AttributeAndIdList) XXX_DiscardUnknown() { + xxx_messageInfo_AttributeAndIdList.DiscardUnknown(m) +} + +var xxx_messageInfo_AttributeAndIdList proto.InternalMessageInfo + +func (m *AttributeAndIdList) GetAttributeAndIdList() []*AttributeAndId { + if m != nil { + return m.AttributeAndIdList + } + return nil +} + +type AttributeList struct { + Attributes []*Attribute `protobuf:"bytes,1,rep,name=attributes,proto3" json:"attributes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AttributeList) Reset() { *m = AttributeList{} } +func (m *AttributeList) String() string { return proto.CompactTextString(m) } +func (*AttributeList) ProtoMessage() {} +func (*AttributeList) Descriptor() ([]byte, []int) { + return fileDescriptor_e23ec92774814a82, []int{2} +} + +func (m *AttributeList) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AttributeList.Unmarshal(m, b) +} +func (m *AttributeList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AttributeList.Marshal(b, m, deterministic) +} +func (m *AttributeList) XXX_Merge(src proto.Message) { + xxx_messageInfo_AttributeList.Merge(m, src) +} +func (m *AttributeList) XXX_Size() int { + return xxx_messageInfo_AttributeList.Size(m) +} +func (m *AttributeList) XXX_DiscardUnknown() { + xxx_messageInfo_AttributeList.DiscardUnknown(m) +} + +var xxx_messageInfo_AttributeList proto.InternalMessageInfo + +func (m *AttributeList) GetAttributes() []*Attribute { + if m != nil { + return m.Attributes + } + return nil +} + +func init() { + proto.RegisterType((*AttributeAndId)(nil), "attrpubapi_v1.AttributeAndId") + proto.RegisterType((*AttributeAndIdList)(nil), "attrpubapi_v1.AttributeAndIdList") + proto.RegisterType((*AttributeList)(nil), "attrpubapi_v1.AttributeList") +} + +func init() { proto.RegisterFile("List.proto", fileDescriptor_e23ec92774814a82) } + +var fileDescriptor_e23ec92774814a82 = []byte{ + // 219 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xf2, 0xc9, 0x2c, 0x2e, + 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4d, 0x2c, 0x29, 0x29, 0x2a, 0x28, 0x4d, 0x4a, + 0x2c, 0xc8, 0x8c, 0x2f, 0x33, 0x94, 0xe2, 0x77, 0x2c, 0x29, 0x29, 0xca, 0x4c, 0x2a, 0x2d, 0x49, + 0x85, 0xc8, 0x2b, 0x65, 0x73, 0xf1, 0xc1, 0x85, 0x1c, 0xf3, 0x52, 0x3c, 0x53, 0x84, 0xcc, 0xb8, + 0x38, 0x13, 0x61, 0x22, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xdc, 0x46, 0x12, 0x7a, 0x28, 0xa6, 0xe8, + 0xc1, 0x75, 0x04, 0x21, 0x94, 0x0a, 0x29, 0x72, 0xf1, 0xc0, 0x39, 0xf1, 0x99, 0x29, 0x12, 0x4c, + 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0xdc, 0x70, 0x31, 0xcf, 0x14, 0xa5, 0x34, 0x2e, 0x21, 0x54, 0xcb, + 0x40, 0x0e, 0x15, 0x0a, 0xe0, 0x12, 0x45, 0x68, 0x4c, 0xcc, 0x4b, 0x89, 0xcf, 0x4c, 0x89, 0xcf, + 0xc9, 0x2c, 0x2e, 0x91, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x92, 0xc5, 0x65, 0x39, 0xd8, 0x84, + 0x20, 0xa1, 0x44, 0x0c, 0x13, 0x95, 0x3c, 0xb9, 0x78, 0xe1, 0xaa, 0xc0, 0x56, 0x58, 0x70, 0x71, + 0xc1, 0x95, 0x15, 0x43, 0xcd, 0xc5, 0xed, 0x29, 0x24, 0xb5, 0x4e, 0x46, 0x5c, 0x62, 0xc9, 0xf9, + 0xb9, 0x7a, 0x95, 0xf9, 0x25, 0x99, 0x28, 0xea, 0x8d, 0x9d, 0x38, 0x41, 0x1a, 0x02, 0x40, 0x81, + 0x18, 0xc5, 0x0b, 0x92, 0x06, 0x87, 0x27, 0x48, 0x49, 0x12, 0x1b, 0x98, 0x69, 0x0c, 0x08, 0x00, + 0x00, 0xff, 0xff, 0xc9, 0xf6, 0x43, 0xa8, 0x88, 0x01, 0x00, 0x00, +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.proto b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.proto new file mode 100644 index 0000000..9d26274 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/List.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +package attrpubapi_v1; + +import "Attribute.proto"; + +option java_package = "com.yoti.api.client.spi.remote.proto"; +option java_outer_classname = "AttrProto"; + +option go_package = "yotiprotoattr"; + +// AttributeAndId is a simple container for holding an attribute's value +// alongside its ID. +message AttributeAndId { + Attribute attribute = 1; + + bytes attribute_id = 2; +} + + +message AttributeAndIdList{ + repeated AttributeAndId attribute_and_id_list = 1; +} + + +message AttributeList { + repeated Attribute attributes = 1; +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.pb.go new file mode 100644 index 0000000..a283849 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.pb.go @@ -0,0 +1,127 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: Signing.proto + +package yotiprotoattr + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type AttributeSigning struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + ContentType ContentType `protobuf:"varint,3,opt,name=content_type,json=contentType,proto3,enum=attrpubapi_v1.ContentType" json:"content_type,omitempty"` + ArtifactSignature []byte `protobuf:"bytes,4,opt,name=artifact_signature,json=artifactSignature,proto3" json:"artifact_signature,omitempty"` + SubType string `protobuf:"bytes,5,opt,name=sub_type,json=subType,proto3" json:"sub_type,omitempty"` + SignedTimeStamp []byte `protobuf:"bytes,6,opt,name=signed_time_stamp,json=signedTimeStamp,proto3" json:"signed_time_stamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AttributeSigning) Reset() { *m = AttributeSigning{} } +func (m *AttributeSigning) String() string { return proto.CompactTextString(m) } +func (*AttributeSigning) ProtoMessage() {} +func (*AttributeSigning) Descriptor() ([]byte, []int) { + return fileDescriptor_c19542824c3e34e0, []int{0} +} + +func (m *AttributeSigning) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AttributeSigning.Unmarshal(m, b) +} +func (m *AttributeSigning) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AttributeSigning.Marshal(b, m, deterministic) +} +func (m *AttributeSigning) XXX_Merge(src proto.Message) { + xxx_messageInfo_AttributeSigning.Merge(m, src) +} +func (m *AttributeSigning) XXX_Size() int { + return xxx_messageInfo_AttributeSigning.Size(m) +} +func (m *AttributeSigning) XXX_DiscardUnknown() { + xxx_messageInfo_AttributeSigning.DiscardUnknown(m) +} + +var xxx_messageInfo_AttributeSigning proto.InternalMessageInfo + +func (m *AttributeSigning) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *AttributeSigning) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +func (m *AttributeSigning) GetContentType() ContentType { + if m != nil { + return m.ContentType + } + return ContentType_UNDEFINED +} + +func (m *AttributeSigning) GetArtifactSignature() []byte { + if m != nil { + return m.ArtifactSignature + } + return nil +} + +func (m *AttributeSigning) GetSubType() string { + if m != nil { + return m.SubType + } + return "" +} + +func (m *AttributeSigning) GetSignedTimeStamp() []byte { + if m != nil { + return m.SignedTimeStamp + } + return nil +} + +func init() { + proto.RegisterType((*AttributeSigning)(nil), "attrpubapi_v1.AttributeSigning") +} + +func init() { proto.RegisterFile("Signing.proto", fileDescriptor_c19542824c3e34e0) } + +var fileDescriptor_c19542824c3e34e0 = []byte{ + // 260 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x54, 0x90, 0xcd, 0x4e, 0xeb, 0x30, + 0x10, 0x85, 0x95, 0x7b, 0xdb, 0x42, 0x4d, 0x43, 0xa9, 0x85, 0x50, 0xe8, 0x2a, 0x62, 0x15, 0x21, + 0x11, 0x89, 0x76, 0xcd, 0x82, 0xf2, 0x02, 0x28, 0xe9, 0x8a, 0x8d, 0x65, 0x87, 0x21, 0xb2, 0x84, + 0x7f, 0xe4, 0x8c, 0x2b, 0xe5, 0xb1, 0x79, 0x03, 0x64, 0x9b, 0x82, 0xba, 0x9b, 0x99, 0x6f, 0xe6, + 0x9c, 0xd1, 0x21, 0x79, 0x2b, 0x7b, 0x2d, 0x75, 0x5f, 0x5b, 0x67, 0xd0, 0xd0, 0x9c, 0x23, 0x3a, + 0xeb, 0x05, 0xb7, 0x92, 0x1d, 0x1e, 0xd7, 0xcb, 0x67, 0x44, 0x27, 0x85, 0x47, 0x48, 0xfc, 0xee, + 0x2b, 0x23, 0x57, 0xbf, 0xb3, 0x9f, 0x53, 0x4a, 0xc9, 0x44, 0x73, 0x05, 0x45, 0x56, 0x66, 0xd5, + 0xbc, 0x89, 0x35, 0xbd, 0x26, 0xd3, 0x03, 0xff, 0xf4, 0x50, 0xfc, 0x2b, 0xb3, 0x6a, 0xd1, 0xa4, + 0x86, 0x3e, 0x91, 0x45, 0x67, 0x34, 0x82, 0x46, 0x86, 0xa3, 0x85, 0xe2, 0x7f, 0x99, 0x55, 0x97, + 0x9b, 0x75, 0x7d, 0xe2, 0x5a, 0xbf, 0xa4, 0x95, 0xfd, 0x68, 0xa1, 0xb9, 0xe8, 0xfe, 0x1a, 0xfa, + 0x40, 0x28, 0x77, 0x28, 0x3f, 0x78, 0x87, 0x6c, 0x90, 0xbd, 0xe6, 0xe8, 0x1d, 0x14, 0x93, 0xe8, + 0xb0, 0x3a, 0x92, 0xf6, 0x08, 0xe8, 0x2d, 0x39, 0x1f, 0xbc, 0x48, 0x4e, 0xd3, 0xf8, 0xdb, 0xd9, + 0xe0, 0x45, 0x54, 0xba, 0x27, 0xab, 0x20, 0x00, 0xef, 0x0c, 0xa5, 0x02, 0x36, 0x20, 0x57, 0xb6, + 0x98, 0x45, 0xa1, 0x65, 0x02, 0x7b, 0xa9, 0xa0, 0x0d, 0xe3, 0xdd, 0x86, 0xdc, 0x74, 0x46, 0xd5, + 0xa3, 0x41, 0x79, 0xf2, 0xe8, 0x76, 0x37, 0x0f, 0x51, 0xbc, 0x86, 0x60, 0xde, 0xf2, 0x80, 0x63, + 0x46, 0x61, 0x45, 0xcc, 0x62, 0xb9, 0xfd, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xed, 0xfc, 0x51, 0x61, + 0x5f, 0x01, 0x00, 0x00, +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.proto b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.proto new file mode 100644 index 0000000..67d4e5d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotoattr/Signing.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package attrpubapi_v1; + +import "Attribute.proto"; + +option java_package = "com.yoti.api.client.spi.remote.proto"; +option java_outer_classname = "AttrProto"; + +option go_package = "yotiprotoattr"; + +message AttributeSigning { + string name = 1; + + bytes value = 2; + + ContentType content_type = 3; + + bytes artifact_signature = 4; + + string sub_type = 5; + + bytes signed_time_stamp = 6; +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.pb.go new file mode 100644 index 0000000..7de16b8 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.pb.go @@ -0,0 +1,91 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: EncryptedData.proto + +package yotiprotocom + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type EncryptedData struct { + // the iv will be used in conjunction with the secret key + // received via other channel in order to decrypt the cipher_text + Iv []byte `protobuf:"bytes,1,opt,name=iv,proto3" json:"iv,omitempty"` + // block of bytes to be decrypted + CipherText []byte `protobuf:"bytes,2,opt,name=cipher_text,json=cipherText,proto3" json:"cipher_text,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EncryptedData) Reset() { *m = EncryptedData{} } +func (m *EncryptedData) String() string { return proto.CompactTextString(m) } +func (*EncryptedData) ProtoMessage() {} +func (*EncryptedData) Descriptor() ([]byte, []int) { + return fileDescriptor_ed1db0e32f8afa88, []int{0} +} + +func (m *EncryptedData) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EncryptedData.Unmarshal(m, b) +} +func (m *EncryptedData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EncryptedData.Marshal(b, m, deterministic) +} +func (m *EncryptedData) XXX_Merge(src proto.Message) { + xxx_messageInfo_EncryptedData.Merge(m, src) +} +func (m *EncryptedData) XXX_Size() int { + return xxx_messageInfo_EncryptedData.Size(m) +} +func (m *EncryptedData) XXX_DiscardUnknown() { + xxx_messageInfo_EncryptedData.DiscardUnknown(m) +} + +var xxx_messageInfo_EncryptedData proto.InternalMessageInfo + +func (m *EncryptedData) GetIv() []byte { + if m != nil { + return m.Iv + } + return nil +} + +func (m *EncryptedData) GetCipherText() []byte { + if m != nil { + return m.CipherText + } + return nil +} + +func init() { + proto.RegisterType((*EncryptedData)(nil), "compubapi_v1.EncryptedData") +} + +func init() { proto.RegisterFile("EncryptedData.proto", fileDescriptor_ed1db0e32f8afa88) } + +var fileDescriptor_ed1db0e32f8afa88 = []byte{ + // 145 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x76, 0xcd, 0x4b, 0x2e, + 0xaa, 0x2c, 0x28, 0x49, 0x4d, 0x71, 0x49, 0x2c, 0x49, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0xe2, 0x49, 0xce, 0xcf, 0x2d, 0x28, 0x4d, 0x4a, 0x2c, 0xc8, 0x8c, 0x2f, 0x33, 0x54, 0x72, 0xe0, + 0xe2, 0x45, 0x51, 0x24, 0xc4, 0xc7, 0xc5, 0x94, 0x59, 0x26, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13, + 0xc4, 0x94, 0x59, 0x26, 0x24, 0xcf, 0xc5, 0x9d, 0x9c, 0x59, 0x90, 0x91, 0x5a, 0x14, 0x5f, 0x92, + 0x5a, 0x51, 0x22, 0xc1, 0x04, 0x96, 0xe0, 0x82, 0x08, 0x85, 0xa4, 0x56, 0x94, 0x38, 0x59, 0x72, + 0x89, 0x26, 0xe7, 0xe7, 0xea, 0x55, 0xe6, 0x97, 0x64, 0xea, 0x21, 0x19, 0x6d, 0xec, 0x24, 0x84, + 0x62, 0x70, 0x00, 0xc8, 0xf2, 0x28, 0x1e, 0x90, 0x32, 0xb0, 0x3b, 0x92, 0xf3, 0x73, 0x93, 0xd8, + 0xc0, 0x2c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0xe9, 0x2c, 0x57, 0xa8, 0x00, 0x00, + 0x00, +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.proto b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.proto new file mode 100644 index 0000000..d9f1e4d --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/EncryptedData.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package compubapi_v1; + +option java_package = "com.yoti.api.client.spi.remote.proto"; +option java_outer_classname = "EncryptedDataProto"; + +option go_package = "yotiprotocom"; + +message EncryptedData { + // the iv will be used in conjunction with the secret key + // received via other channel in order to decrypt the cipher_text + bytes iv = 1; + + // block of bytes to be decrypted + bytes cipher_text = 2; +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.pb.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.pb.go new file mode 100644 index 0000000..180ec47 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.pb.go @@ -0,0 +1,147 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: SignedTimestamp.proto + +package yotiprotocom + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// SignedTimestamp is a timestamp associated with a message that has a +// cryptographic signature proving that it was issued by the correct authority. +type SignedTimestamp struct { + // Version indicates how the digests within this object are calculated. + Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + // Timestamp is the time this SignedTimestamp was issued. It is in UTC, + // as µseconds elapsed since the epoch (µs from 1970-01-01T00:00:00Z). + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // MessageDigest is the digest of the message this timestamp is + // associated with. The first step in verifying the timestamp is + // ensuring the MessageDigest matches the original message data. + // + // For version 1 objects, the message digest algorithm is SHA-512/224. + MessageDigest []byte `protobuf:"bytes,3,opt,name=message_digest,json=messageDigest,proto3" json:"message_digest,omitempty"` + // ChainDigest is the digest of the previous SignedTimestamp message + // in the chain. The second step in verifying the timestamp is walking + // back over the chain and checking each SignedTimestamp's ChainDigest + // field. The SignedTimestamp at the beginning of the chain has this + // field set to a specific, publish value. + // + // For version 1 objects, the chain digest algorithm is HMAC-SHA-512/224, + // with the secret being equal to the MessageDigest field. + ChainDigest []byte `protobuf:"bytes,4,opt,name=chain_digest,json=chainDigest,proto3" json:"chain_digest,omitempty"` + // ChainDigestSkip1 is only populated once every 500 nodes. It is the + // ChainDigest value of the timestamp 500 nodes previously. + ChainDigestSkip1 []byte `protobuf:"bytes,5,opt,name=chain_digest_skip1,json=chainDigestSkip1,proto3" json:"chain_digest_skip1,omitempty"` + // ChainDigestSkip2 is only populated once every 250000 nodes (or once + // every 500 nodes that have ChainDigestSkip1 populated). It is the + // ChainDigest value of the timestamp 250000 nodes previously. + ChainDigestSkip2 []byte `protobuf:"bytes,6,opt,name=chain_digest_skip2,json=chainDigestSkip2,proto3" json:"chain_digest_skip2,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SignedTimestamp) Reset() { *m = SignedTimestamp{} } +func (m *SignedTimestamp) String() string { return proto.CompactTextString(m) } +func (*SignedTimestamp) ProtoMessage() {} +func (*SignedTimestamp) Descriptor() ([]byte, []int) { + return fileDescriptor_5602a15d48f50e63, []int{0} +} + +func (m *SignedTimestamp) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SignedTimestamp.Unmarshal(m, b) +} +func (m *SignedTimestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SignedTimestamp.Marshal(b, m, deterministic) +} +func (m *SignedTimestamp) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignedTimestamp.Merge(m, src) +} +func (m *SignedTimestamp) XXX_Size() int { + return xxx_messageInfo_SignedTimestamp.Size(m) +} +func (m *SignedTimestamp) XXX_DiscardUnknown() { + xxx_messageInfo_SignedTimestamp.DiscardUnknown(m) +} + +var xxx_messageInfo_SignedTimestamp proto.InternalMessageInfo + +func (m *SignedTimestamp) GetVersion() int32 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *SignedTimestamp) GetTimestamp() uint64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *SignedTimestamp) GetMessageDigest() []byte { + if m != nil { + return m.MessageDigest + } + return nil +} + +func (m *SignedTimestamp) GetChainDigest() []byte { + if m != nil { + return m.ChainDigest + } + return nil +} + +func (m *SignedTimestamp) GetChainDigestSkip1() []byte { + if m != nil { + return m.ChainDigestSkip1 + } + return nil +} + +func (m *SignedTimestamp) GetChainDigestSkip2() []byte { + if m != nil { + return m.ChainDigestSkip2 + } + return nil +} + +func init() { + proto.RegisterType((*SignedTimestamp)(nil), "compubapi_v1.SignedTimestamp") +} + +func init() { proto.RegisterFile("SignedTimestamp.proto", fileDescriptor_5602a15d48f50e63) } + +var fileDescriptor_5602a15d48f50e63 = []byte{ + // 222 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x0d, 0xce, 0x4c, 0xcf, + 0x4b, 0x4d, 0x09, 0xc9, 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0xe2, 0x49, 0xce, 0xcf, 0x2d, 0x28, 0x4d, 0x4a, 0x2c, 0xc8, 0x8c, 0x2f, 0x33, 0x54, + 0x7a, 0xcf, 0xc8, 0xc5, 0x8f, 0xa6, 0x4e, 0x48, 0x82, 0x8b, 0xbd, 0x2c, 0xb5, 0xa8, 0x38, 0x33, + 0x3f, 0x4f, 0x82, 0x51, 0x81, 0x51, 0x83, 0x35, 0x08, 0xc6, 0x15, 0x92, 0xe1, 0xe2, 0x2c, 0x81, + 0x29, 0x93, 0x60, 0x52, 0x60, 0xd4, 0x60, 0x09, 0x42, 0x08, 0x08, 0xa9, 0x72, 0xf1, 0xe5, 0xa6, + 0x16, 0x17, 0x27, 0xa6, 0xa7, 0xc6, 0xa7, 0x64, 0xa6, 0xa7, 0x16, 0x97, 0x48, 0x30, 0x2b, 0x30, + 0x6a, 0xf0, 0x04, 0xf1, 0x42, 0x45, 0x5d, 0xc0, 0x82, 0x42, 0x8a, 0x5c, 0x3c, 0xc9, 0x19, 0x89, + 0x99, 0x79, 0x30, 0x45, 0x2c, 0x60, 0x45, 0xdc, 0x60, 0x31, 0xa8, 0x12, 0x1d, 0x2e, 0x21, 0x64, + 0x25, 0xf1, 0xc5, 0xd9, 0x99, 0x05, 0x86, 0x12, 0xac, 0x60, 0x85, 0x02, 0x48, 0x0a, 0x83, 0x41, + 0xe2, 0x58, 0x55, 0x1b, 0x49, 0xb0, 0x61, 0x55, 0x6d, 0xe4, 0x64, 0xcd, 0x25, 0x9a, 0x9c, 0x9f, + 0xab, 0x57, 0x99, 0x5f, 0x92, 0xa9, 0x87, 0x14, 0x14, 0xc6, 0x4e, 0x22, 0x68, 0xe1, 0x10, 0x00, + 0x0a, 0xae, 0x28, 0x1e, 0x90, 0x42, 0x70, 0xc8, 0x25, 0xe7, 0xe7, 0x26, 0xb1, 0x81, 0x59, 0xc6, + 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa1, 0x27, 0xbf, 0x01, 0x5c, 0x01, 0x00, 0x00, +} diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.proto b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.proto new file mode 100644 index 0000000..1629722 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiprotocom/SignedTimestamp.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; + +package compubapi_v1; + +option java_package = "com.yoti.api.client.spi.remote.proto"; +option java_outer_classname = "SignedTimestampProto"; + +option go_package = "yotiprotocom"; + +// SignedTimestamp is a timestamp associated with a message that has a +// cryptographic signature proving that it was issued by the correct authority. +message SignedTimestamp { + // Version indicates how the digests within this object are calculated. + int32 version = 1; + + // Timestamp is the time this SignedTimestamp was issued. It is in UTC, + // as µseconds elapsed since the epoch (µs from 1970-01-01T00:00:00Z). + uint64 timestamp = 2; + + // MessageDigest is the digest of the message this timestamp is + // associated with. The first step in verifying the timestamp is + // ensuring the MessageDigest matches the original message data. + // + // For version 1 objects, the message digest algorithm is SHA-512/224. + bytes message_digest = 3; + + // ChainDigest is the digest of the previous SignedTimestamp message + // in the chain. The second step in verifying the timestamp is walking + // back over the chain and checking each SignedTimestamp's ChainDigest + // field. The SignedTimestamp at the beginning of the chain has this + // field set to a specific, publish value. + // + // For version 1 objects, the chain digest algorithm is HMAC-SHA-512/224, + // with the secret being equal to the MessageDigest field. + bytes chain_digest = 4; + + // ChainDigestSkip1 is only populated once every 500 nodes. It is the + // ChainDigest value of the timestamp 500 nodes previously. + bytes chain_digest_skip1 = 5; + + // ChainDigestSkip2 is only populated once every 250000 nodes (or once + // every 500 nodes that have ChainDigestSkip1 populated). It is the + // ChainDigest value of the timestamp 250000 nodes previously. + bytes chain_digest_skip2 = 6; +} \ No newline at end of file diff --git a/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiuserprofile.go b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiuserprofile.go new file mode 100644 index 0000000..2091586 --- /dev/null +++ b/root/pkg/mod/github.com/getyoti/yoti-go-sdk@v2.3.1+incompatible/yotiuserprofile.go @@ -0,0 +1,53 @@ +package yoti + +import ( + "time" +) + +// Deprecated: Will be removed in v3.0.0. Use `Profile` instead. UserProfile represents the details retrieved for a particular +type UserProfile struct { + // ID is a unique identifier Yoti assigns to your user, but only for your app. + // If the same user logs into your app again, you get the same id. + // If she/he logs into another application, Yoti will assign a different id for that app. + ID string + + // Selfie is a photograph of the user. This will be nil if not provided by Yoti + Selfie *Image + + // GivenNames represents the user's given names. This will be an empty string if not provided by Yoti + GivenNames string + + // Family represents the user's family name. This will be an empty string if not provided by Yoti + FamilyName string + + // Full name represents the user's full name. This will be an empty string if not provided by Yoti + FullName string + + // MobileNumber represents the user's mobile phone number. This will be an empty string if not provided by Yoti + MobileNumber string + + // EmailAddress represents the user's email address. This will be an empty string if not provided by Yoti + EmailAddress string + + // DateOfBirth represents the user's date of birth. This will be nil if not provided by Yoti + DateOfBirth *time.Time + + // IsAgeVerified represents the result of the age verification check on the user. The bool will be true if they passed, false if they failed, and nil if there was no check + IsAgeVerified *bool + + // Address represents the user's address. This will be an empty string if not provided by Yoti + Address string + + // StructuredPostalAddress represents the user's address in a JSON format. This will be empty if not provided by Yoti + StructuredPostalAddress interface{} + + // Gender represents the user's gender. This will be an empty string if not provided by Yoti + Gender string + + // Nationality represents the user's nationality. This will be an empty string if not provided by Yoti + Nationality string + + // OtherAttributes is a map of any other information about the user provided by Yoti. The key will be the name + // of the piece of information, and the keys associated value will be the piece of information itself. + OtherAttributes map[string]AttributeValue +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/.github/workflows/test.yml b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/.github/workflows/test.yml new file mode 100644 index 0000000..6b1b1c4 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/.github/workflows/test.yml @@ -0,0 +1,30 @@ +on: [push, pull_request] +name: Test +jobs: + test: + env: + GOPATH: ${{ github.workspace }} + defaults: + run: + working-directory: ${{ env.GOPATH }}/src/github.com/${{ github.repository }} + strategy: + matrix: + go-version: [1.8.x, 1.9.x, 1.10.x, 1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x] + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v2 + with: + path: ${{ env.GOPATH }}/src/github.com/${{ github.repository }} + - name: Checkout dependencies + run: go get golang.org/x/xerrors + - name: Test + run: go test -v -race ./... + - name: Format + if: matrix.go-version == '1.16.x' + run: diff -u <(echo -n) <(gofmt -d .) diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/CONTRIBUTING.md b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/CONTRIBUTING.md new file mode 100644 index 0000000..ae319c7 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement. You (or your employer) retain the copyright to your contribution, +this simply gives us permission to use and redistribute your contributions as +part of the project. Head over to to see +your current agreements on file or to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/LICENSE b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/LICENSE new file mode 100644 index 0000000..32017f8 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/README.md b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/README.md new file mode 100644 index 0000000..ed0eb9b --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/README.md @@ -0,0 +1,44 @@ +# Package for equality of Go values + +[![GoDev](https://img.shields.io/static/v1?label=godev&message=reference&color=00add8)][godev] +[![Build Status](https://travis-ci.org/google/go-cmp.svg?branch=master)][travis] + +This package is intended to be a more powerful and safer alternative to +`reflect.DeepEqual` for comparing whether two values are semantically equal. + +The primary features of `cmp` are: + +* When the default behavior of equality does not suit the needs of the test, + custom equality functions can override the equality operation. + For example, an equality function may report floats as equal so long as they + are within some tolerance of each other. + +* Types that have an `Equal` method may use that method to determine equality. + This allows package authors to determine the equality operation for the types + that they define. + +* If no custom equality functions are used and no `Equal` method is defined, + equality is determined by recursively comparing the primitive kinds on both + values, much like `reflect.DeepEqual`. Unlike `reflect.DeepEqual`, unexported + fields are not compared by default; they result in panics unless suppressed + by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explicitly + compared using the `AllowUnexported` option. + +See the [documentation][godev] for more information. + +This is not an official Google product. + +[godev]: https://pkg.go.dev/github.com/google/go-cmp/cmp +[travis]: https://travis-ci.org/google/go-cmp + +## Install + +``` +go get -u github.com/google/go-cmp/cmp +``` + +## License + +BSD - See [LICENSE][license] file + +[license]: https://github.com/google/go-cmp/blob/master/LICENSE diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/equate.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/equate.go new file mode 100644 index 0000000..e4ffca8 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/equate.go @@ -0,0 +1,148 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmpopts provides common options for the cmp package. +package cmpopts + +import ( + "math" + "reflect" + "time" + + "github.com/google/go-cmp/cmp" +) + +func equateAlways(_, _ interface{}) bool { return true } + +// EquateEmpty returns a Comparer option that determines all maps and slices +// with a length of zero to be equal, regardless of whether they are nil. +// +// EquateEmpty can be used in conjunction with SortSlices and SortMaps. +func EquateEmpty() cmp.Option { + return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) +} + +func isEmpty(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && + (vx.Len() == 0 && vy.Len() == 0) +} + +// EquateApprox returns a Comparer option that determines float32 or float64 +// values to be equal if they are within a relative fraction or absolute margin. +// This option is not used when either x or y is NaN or infinite. +// +// The fraction determines that the difference of two values must be within the +// smaller fraction of the two values, while the margin determines that the two +// values must be within some absolute margin. +// To express only a fraction or only a margin, use 0 for the other parameter. +// The fraction and margin must be non-negative. +// +// The mathematical expression used is equivalent to: +// |x-y| ≤ max(fraction*min(|x|, |y|), margin) +// +// EquateApprox can be used in conjunction with EquateNaNs. +func EquateApprox(fraction, margin float64) cmp.Option { + if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { + panic("margin or fraction must be a non-negative number") + } + a := approximator{fraction, margin} + return cmp.Options{ + cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)), + cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)), + } +} + +type approximator struct{ frac, marg float64 } + +func areRealF64s(x, y float64) bool { + return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0) +} +func areRealF32s(x, y float32) bool { + return areRealF64s(float64(x), float64(y)) +} +func (a approximator) compareF64(x, y float64) bool { + relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y)) + return math.Abs(x-y) <= math.Max(a.marg, relMarg) +} +func (a approximator) compareF32(x, y float32) bool { + return a.compareF64(float64(x), float64(y)) +} + +// EquateNaNs returns a Comparer option that determines float32 and float64 +// NaN values to be equal. +// +// EquateNaNs can be used in conjunction with EquateApprox. +func EquateNaNs() cmp.Option { + return cmp.Options{ + cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), + cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)), + } +} + +func areNaNsF64s(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) +} +func areNaNsF32s(x, y float32) bool { + return areNaNsF64s(float64(x), float64(y)) +} + +// EquateApproxTime returns a Comparer option that determines two non-zero +// time.Time values to be equal if they are within some margin of one another. +// If both times have a monotonic clock reading, then the monotonic time +// difference will be used. The margin must be non-negative. +func EquateApproxTime(margin time.Duration) cmp.Option { + if margin < 0 { + panic("margin must be a non-negative number") + } + a := timeApproximator{margin} + return cmp.FilterValues(areNonZeroTimes, cmp.Comparer(a.compare)) +} + +func areNonZeroTimes(x, y time.Time) bool { + return !x.IsZero() && !y.IsZero() +} + +type timeApproximator struct { + margin time.Duration +} + +func (a timeApproximator) compare(x, y time.Time) bool { + // Avoid subtracting times to avoid overflow when the + // difference is larger than the largest representible duration. + if x.After(y) { + // Ensure x is always before y + x, y = y, x + } + // We're within the margin if x+margin >= y. + // Note: time.Time doesn't have AfterOrEqual method hence the negation. + return !x.Add(a.margin).Before(y) +} + +// AnyError is an error that matches any non-nil error. +var AnyError anyError + +type anyError struct{} + +func (anyError) Error() string { return "any error" } +func (anyError) Is(err error) bool { return err != nil } + +// EquateErrors returns a Comparer option that determines errors to be equal +// if errors.Is reports them to match. The AnyError error can be used to +// match any non-nil error. +func EquateErrors() cmp.Option { + return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors)) +} + +// areConcreteErrors reports whether x and y are types that implement error. +// The input types are deliberately of the interface{} type rather than the +// error type so that we can handle situations where the current type is an +// interface{}, but the underlying concrete types both happen to implement +// the error interface. +func areConcreteErrors(x, y interface{}) bool { + _, ok1 := x.(error) + _, ok2 := y.(error) + return ok1 && ok2 +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_go113.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_go113.go new file mode 100644 index 0000000..26fe25d --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_go113.go @@ -0,0 +1,15 @@ +// Copyright 2021, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.13 + +package cmpopts + +import "errors" + +func compareErrors(x, y interface{}) bool { + xe := x.(error) + ye := y.(error) + return errors.Is(xe, ye) || errors.Is(ye, xe) +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_xerrors.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_xerrors.go new file mode 100644 index 0000000..6eeb8d6 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/errors_xerrors.go @@ -0,0 +1,18 @@ +// Copyright 2021, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.13 + +// TODO(≥go1.13): For support on = 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) { + panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i))) + } + start = -1 + } else if start == -1 { + start = i + } + } +} +func (ss sliceSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i), v.Index(j) + return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} + +// SortMaps returns a Transformer option that flattens map[K]V types to be a +// sorted []struct{K, V}. The less function must be of the form +// "func(T, T) bool" which is used to sort any map with key K that is +// assignable to T. +// +// Flattening the map into a slice has the property that cmp.Equal is able to +// use Comparers on K or the K.Equal method if it exists. +// +// The less function must be: +// • Deterministic: less(x, y) == less(x, y) +// • Irreflexive: !less(x, x) +// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// • Total: if x != y, then either less(x, y) or less(y, x) +// +// SortMaps can be used in conjunction with EquateEmpty. +func SortMaps(lessFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessFunc) + if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { + panic(fmt.Sprintf("invalid less function: %T", lessFunc)) + } + ms := mapSorter{vf.Type().In(0), vf} + return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) +} + +type mapSorter struct { + in reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (ms mapSorter) filter(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (x != nil && y != nil && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) && + (vx.Len() != 0 || vy.Len() != 0) +} +func (ms mapSorter) sort(x interface{}) interface{} { + src := reflect.ValueOf(x) + outType := reflect.StructOf([]reflect.StructField{ + {Name: "K", Type: src.Type().Key()}, + {Name: "V", Type: src.Type().Elem()}, + }) + dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len()) + for i, k := range src.MapKeys() { + v := reflect.New(outType).Elem() + v.Field(0).Set(k) + v.Field(1).Set(src.MapIndex(k)) + dst.Index(i).Set(v) + } + sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) }) + ms.checkSort(dst) + return dst.Interface() +} +func (ms mapSorter) checkSort(v reflect.Value) { + for i := 1; i < v.Len(); i++ { + if !ms.less(v, i-1, i) { + panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i))) + } + } +} +func (ms mapSorter) less(v reflect.Value, i, j int) bool { + vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) + return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/struct_filter.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/struct_filter.go new file mode 100644 index 0000000..a09829c --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/struct_filter.go @@ -0,0 +1,187 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" +) + +// filterField returns a new Option where opt is only evaluated on paths that +// include a specific exported field on a single struct type. +// The struct type is specified by passing in a value of that type. +// +// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a +// specific sub-field that is embedded or nested within the parent struct. +func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option { + // TODO: This is currently unexported over concerns of how helper filters + // can be composed together easily. + // TODO: Add tests for FilterField. + + sf := newStructFilter(typ, name) + return cmp.FilterPath(sf.filter, opt) +} + +type structFilter struct { + t reflect.Type // The root struct type to match on + ft fieldTree // Tree of fields to match on +} + +func newStructFilter(typ interface{}, names ...string) structFilter { + // TODO: Perhaps allow * as a special identifier to allow ignoring any + // number of path steps until the next field match? + // This could be useful when a concrete struct gets transformed into + // an anonymous struct where it is not possible to specify that by type, + // but the transformer happens to provide guarantees about the names of + // the transformed fields. + + t := reflect.TypeOf(typ) + if t == nil || t.Kind() != reflect.Struct { + panic(fmt.Sprintf("%T must be a non-pointer struct", typ)) + } + var ft fieldTree + for _, name := range names { + cname, err := canonicalName(t, name) + if err != nil { + panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err)) + } + ft.insert(cname) + } + return structFilter{t, ft} +} + +func (sf structFilter) filter(p cmp.Path) bool { + for i, ps := range p { + if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) { + return true + } + } + return false +} + +// fieldTree represents a set of dot-separated identifiers. +// +// For example, inserting the following selectors: +// Foo +// Foo.Bar.Baz +// Foo.Buzz +// Nuka.Cola.Quantum +// +// Results in a tree of the form: +// {sub: { +// "Foo": {ok: true, sub: { +// "Bar": {sub: { +// "Baz": {ok: true}, +// }}, +// "Buzz": {ok: true}, +// }}, +// "Nuka": {sub: { +// "Cola": {sub: { +// "Quantum": {ok: true}, +// }}, +// }}, +// }} +type fieldTree struct { + ok bool // Whether this is a specified node + sub map[string]fieldTree // The sub-tree of fields under this node +} + +// insert inserts a sequence of field accesses into the tree. +func (ft *fieldTree) insert(cname []string) { + if ft.sub == nil { + ft.sub = make(map[string]fieldTree) + } + if len(cname) == 0 { + ft.ok = true + return + } + sub := ft.sub[cname[0]] + sub.insert(cname[1:]) + ft.sub[cname[0]] = sub +} + +// matchPrefix reports whether any selector in the fieldTree matches +// the start of path p. +func (ft fieldTree) matchPrefix(p cmp.Path) bool { + for _, ps := range p { + switch ps := ps.(type) { + case cmp.StructField: + ft = ft.sub[ps.Name()] + if ft.ok { + return true + } + if len(ft.sub) == 0 { + return false + } + case cmp.Indirect: + default: + return false + } + } + return false +} + +// canonicalName returns a list of identifiers where any struct field access +// through an embedded field is expanded to include the names of the embedded +// types themselves. +// +// For example, suppose field "Foo" is not directly in the parent struct, +// but actually from an embedded struct of type "Bar". Then, the canonical name +// of "Foo" is actually "Bar.Foo". +// +// Suppose field "Foo" is not directly in the parent struct, but actually +// a field in two different embedded structs of types "Bar" and "Baz". +// Then the selector "Foo" causes a panic since it is ambiguous which one it +// refers to. The user must specify either "Bar.Foo" or "Baz.Foo". +func canonicalName(t reflect.Type, sel string) ([]string, error) { + var name string + sel = strings.TrimPrefix(sel, ".") + if sel == "" { + return nil, fmt.Errorf("name must not be empty") + } + if i := strings.IndexByte(sel, '.'); i < 0 { + name, sel = sel, "" + } else { + name, sel = sel[:i], sel[i:] + } + + // Type must be a struct or pointer to struct. + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return nil, fmt.Errorf("%v must be a struct", t) + } + + // Find the canonical name for this current field name. + // If the field exists in an embedded struct, then it will be expanded. + sf, _ := t.FieldByName(name) + if !isExported(name) { + // Avoid using reflect.Type.FieldByName for unexported fields due to + // buggy behavior with regard to embeddeding and unexported fields. + // See https://golang.org/issue/4876 for details. + sf = reflect.StructField{} + for i := 0; i < t.NumField() && sf.Name == ""; i++ { + if t.Field(i).Name == name { + sf = t.Field(i) + } + } + } + if sf.Name == "" { + return []string{name}, fmt.Errorf("does not exist") + } + var ss []string + for i := range sf.Index { + ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name) + } + if sel == "" { + return ss, nil + } + ssPost, err := canonicalName(sf.Type, sel) + return append(ss, ssPost...), err +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/util_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/util_test.go new file mode 100644 index 0000000..b19bcab --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/util_test.go @@ -0,0 +1,1371 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "bytes" + "errors" + "fmt" + "io" + "math" + "reflect" + "strings" + "sync" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "golang.org/x/xerrors" +) + +type ( + MyInt int + MyInts []int + MyFloat float32 + MyString string + MyTime struct{ time.Time } + MyStruct struct { + A, B []int + C, D map[time.Time]string + } + + Foo1 struct{ Alpha, Bravo, Charlie int } + Foo2 struct{ *Foo1 } + Foo3 struct{ *Foo2 } + Bar1 struct{ Foo3 } + Bar2 struct { + Bar1 + *Foo3 + Bravo float32 + } + Bar3 struct { + Bar1 + Bravo *Bar2 + Delta struct{ Echo Foo1 } + *Foo3 + Alpha string + } + + privateStruct struct{ Public, private int } + PublicStruct struct{ Public, private int } + ParentStruct struct { + *privateStruct + *PublicStruct + Public int + private int + } + + Everything struct { + MyInt + MyFloat + MyTime + MyStruct + Bar3 + ParentStruct + } + + EmptyInterface interface{} +) + +func TestOptions(t *testing.T) { + createBar3X := func() *Bar3 { + return &Bar3{ + Bar1: Bar1{Foo3{&Foo2{&Foo1{Bravo: 2}}}}, + Bravo: &Bar2{ + Bar1: Bar1{Foo3{&Foo2{&Foo1{Charlie: 7}}}}, + Foo3: &Foo3{&Foo2{&Foo1{Bravo: 5}}}, + Bravo: 4, + }, + Delta: struct{ Echo Foo1 }{Foo1{Charlie: 3}}, + Foo3: &Foo3{&Foo2{&Foo1{Alpha: 1}}}, + Alpha: "alpha", + } + } + createBar3Y := func() *Bar3 { + return &Bar3{ + Bar1: Bar1{Foo3{&Foo2{&Foo1{Bravo: 3}}}}, + Bravo: &Bar2{ + Bar1: Bar1{Foo3{&Foo2{&Foo1{Charlie: 8}}}}, + Foo3: &Foo3{&Foo2{&Foo1{Bravo: 6}}}, + Bravo: 5, + }, + Delta: struct{ Echo Foo1 }{Foo1{Charlie: 4}}, + Foo3: &Foo3{&Foo2{&Foo1{Alpha: 2}}}, + Alpha: "ALPHA", + } + } + + tests := []struct { + label string // Test name + x, y interface{} // Input values to compare + opts []cmp.Option // Input options + wantEqual bool // Whether the inputs are equal + wantPanic bool // Whether Equal should panic + reason string // The reason for the expected outcome + }{{ + label: "EquateEmpty", + x: []int{}, + y: []int(nil), + wantEqual: false, + reason: "not equal because empty non-nil and nil slice differ", + }, { + label: "EquateEmpty", + x: []int{}, + y: []int(nil), + opts: []cmp.Option{EquateEmpty()}, + wantEqual: true, + reason: "equal because EquateEmpty equates empty slices", + }, { + label: "SortSlices", + x: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + y: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, + wantEqual: false, + reason: "not equal because element order differs", + }, { + label: "SortSlices", + x: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + y: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, + opts: []cmp.Option{SortSlices(func(x, y int) bool { return x < y })}, + wantEqual: true, + reason: "equal because SortSlices sorts the slices", + }, { + label: "SortSlices", + x: []MyInt{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + y: []MyInt{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, + opts: []cmp.Option{SortSlices(func(x, y int) bool { return x < y })}, + wantEqual: false, + reason: "not equal because MyInt is not the same type as int", + }, { + label: "SortSlices", + x: []float64{0, 1, 1, 2, 2, 2}, + y: []float64{2, 0, 2, 1, 2, 1}, + opts: []cmp.Option{SortSlices(func(x, y float64) bool { return x < y })}, + wantEqual: true, + reason: "equal even when sorted with duplicate elements", + }, { + label: "SortSlices", + x: []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, 3, 4, 4, 4, 4}, + y: []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, 2}, + opts: []cmp.Option{SortSlices(func(x, y float64) bool { return x < y })}, + wantPanic: true, + reason: "panics because SortSlices used with non-transitive less function", + }, { + label: "SortSlices", + x: []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, 3, 4, 4, 4, 4}, + y: []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, 2}, + opts: []cmp.Option{SortSlices(func(x, y float64) bool { + return (!math.IsNaN(x) && math.IsNaN(y)) || x < y + })}, + wantEqual: false, + reason: "no panics because SortSlices used with valid less function; not equal because NaN != NaN", + }, { + label: "SortSlices+EquateNaNs", + x: []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, math.NaN(), 3, 4, 4, 4, 4}, + y: []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, math.NaN(), 2}, + opts: []cmp.Option{ + EquateNaNs(), + SortSlices(func(x, y float64) bool { + return (!math.IsNaN(x) && math.IsNaN(y)) || x < y + }), + }, + wantEqual: true, + reason: "no panics because SortSlices used with valid less function; equal because EquateNaNs is used", + }, { + label: "SortMaps", + x: map[time.Time]string{ + time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday", + time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday", + time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC): "2nd birthday", + }, + y: map[time.Time]string{ + time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday", + time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday", + time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "2nd birthday", + }, + wantEqual: false, + reason: "not equal because timezones differ", + }, { + label: "SortMaps", + x: map[time.Time]string{ + time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday", + time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday", + time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC): "2nd birthday", + }, + y: map[time.Time]string{ + time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday", + time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday", + time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "2nd birthday", + }, + opts: []cmp.Option{SortMaps(func(x, y time.Time) bool { return x.Before(y) })}, + wantEqual: true, + reason: "equal because SortMaps flattens to a slice where Time.Equal can be used", + }, { + label: "SortMaps", + x: map[MyTime]string{ + {time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}: "0th birthday", + {time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC)}: "1st birthday", + {time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC)}: "2nd birthday", + }, + y: map[MyTime]string{ + {time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "0th birthday", + {time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "1st birthday", + {time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "2nd birthday", + }, + opts: []cmp.Option{SortMaps(func(x, y time.Time) bool { return x.Before(y) })}, + wantEqual: false, + reason: "not equal because MyTime is not assignable to time.Time", + }, { + label: "SortMaps", + x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, + // => {0, 1, 2, 3, -1, -2, -3}, + y: map[int]string{300: "", 200: "", 100: "", 0: "", 1: "", 2: "", 3: ""}, + // => {0, 1, 2, 3, 100, 200, 300}, + opts: []cmp.Option{SortMaps(func(a, b int) bool { + if -10 < a && a <= 0 { + a *= -100 + } + if -10 < b && b <= 0 { + b *= -100 + } + return a < b + })}, + wantEqual: false, + reason: "not equal because values differ even though SortMap provides valid ordering", + }, { + label: "SortMaps", + x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, + // => {0, 1, 2, 3, -1, -2, -3}, + y: map[int]string{300: "", 200: "", 100: "", 0: "", 1: "", 2: "", 3: ""}, + // => {0, 1, 2, 3, 100, 200, 300}, + opts: []cmp.Option{ + SortMaps(func(x, y int) bool { + if -10 < x && x <= 0 { + x *= -100 + } + if -10 < y && y <= 0 { + y *= -100 + } + return x < y + }), + cmp.Comparer(func(x, y int) bool { + if -10 < x && x <= 0 { + x *= -100 + } + if -10 < y && y <= 0 { + y *= -100 + } + return x == y + }), + }, + wantEqual: true, + reason: "equal because Comparer used to equate differences", + }, { + label: "SortMaps", + x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, + y: map[int]string{}, + opts: []cmp.Option{SortMaps(func(x, y int) bool { + return x < y && x >= 0 && y >= 0 + })}, + wantPanic: true, + reason: "panics because SortMaps used with non-transitive less function", + }, { + label: "SortMaps", + x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, + y: map[int]string{}, + opts: []cmp.Option{SortMaps(func(x, y int) bool { + return math.Abs(float64(x)) < math.Abs(float64(y)) + })}, + wantPanic: true, + reason: "panics because SortMaps used with partial less function", + }, { + label: "EquateEmpty+SortSlices+SortMaps", + x: MyStruct{ + A: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + C: map[time.Time]string{ + time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday", + time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday", + }, + D: map[time.Time]string{}, + }, + y: MyStruct{ + A: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, + B: []int{}, + C: map[time.Time]string{ + time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday", + time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday", + }, + }, + opts: []cmp.Option{ + EquateEmpty(), + SortSlices(func(x, y int) bool { return x < y }), + SortMaps(func(x, y time.Time) bool { return x.Before(y) }), + }, + wantEqual: true, + reason: "no panics because EquateEmpty should compose with the sort options", + }, { + label: "EquateApprox", + x: 3.09, + y: 3.10, + wantEqual: false, + reason: "not equal because floats do not exactly matches", + }, { + label: "EquateApprox", + x: 3.09, + y: 3.10, + opts: []cmp.Option{EquateApprox(0, 0)}, + wantEqual: false, + reason: "not equal because EquateApprox(0 ,0) is equivalent to using ==", + }, { + label: "EquateApprox", + x: 3.09, + y: 3.10, + opts: []cmp.Option{EquateApprox(0.003, 0.009)}, + wantEqual: false, + reason: "not equal because EquateApprox is too strict", + }, { + label: "EquateApprox", + x: 3.09, + y: 3.10, + opts: []cmp.Option{EquateApprox(0, 0.011)}, + wantEqual: true, + reason: "equal because margin is loose enough to match", + }, { + label: "EquateApprox", + x: 3.09, + y: 3.10, + opts: []cmp.Option{EquateApprox(0.004, 0)}, + wantEqual: true, + reason: "equal because fraction is loose enough to match", + }, { + label: "EquateApprox", + x: 3.09, + y: 3.10, + opts: []cmp.Option{EquateApprox(0.004, 0.011)}, + wantEqual: true, + reason: "equal because both the margin and fraction are loose enough to match", + }, { + label: "EquateApprox", + x: float32(3.09), + y: float64(3.10), + opts: []cmp.Option{EquateApprox(0.004, 0)}, + wantEqual: false, + reason: "not equal because the types differ", + }, { + label: "EquateApprox", + x: float32(3.09), + y: float32(3.10), + opts: []cmp.Option{EquateApprox(0.004, 0)}, + wantEqual: true, + reason: "equal because EquateApprox also applies on float32s", + }, { + label: "EquateApprox", + x: []float64{math.Inf(+1), math.Inf(-1)}, + y: []float64{math.Inf(+1), math.Inf(-1)}, + opts: []cmp.Option{EquateApprox(0, 1)}, + wantEqual: true, + reason: "equal because we fall back on == which matches Inf (EquateApprox does not apply on Inf) ", + }, { + label: "EquateApprox", + x: []float64{math.Inf(+1), -1e100}, + y: []float64{+1e100, math.Inf(-1)}, + opts: []cmp.Option{EquateApprox(0, 1)}, + wantEqual: false, + reason: "not equal because we fall back on == where Inf != 1e100 (EquateApprox does not apply on Inf)", + }, { + label: "EquateApprox", + x: float64(+1e100), + y: float64(-1e100), + opts: []cmp.Option{EquateApprox(math.Inf(+1), 0)}, + wantEqual: true, + reason: "equal because infinite fraction matches everything", + }, { + label: "EquateApprox", + x: float64(+1e100), + y: float64(-1e100), + opts: []cmp.Option{EquateApprox(0, math.Inf(+1))}, + wantEqual: true, + reason: "equal because infinite margin matches everything", + }, { + label: "EquateApprox", + x: math.Pi, + y: math.Pi, + opts: []cmp.Option{EquateApprox(0, 0)}, + wantEqual: true, + reason: "equal because EquateApprox(0, 0) is equivalent to ==", + }, { + label: "EquateApprox", + x: math.Pi, + y: math.Nextafter(math.Pi, math.Inf(+1)), + opts: []cmp.Option{EquateApprox(0, 0)}, + wantEqual: false, + reason: "not equal because EquateApprox(0, 0) is equivalent to ==", + }, { + label: "EquateNaNs", + x: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, + y: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, + wantEqual: false, + reason: "not equal because NaN != NaN", + }, { + label: "EquateNaNs", + x: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, + y: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, + opts: []cmp.Option{EquateNaNs()}, + wantEqual: true, + reason: "equal because EquateNaNs allows NaN == NaN", + }, { + label: "EquateNaNs", + x: []float32{1.0, float32(math.NaN()), math.E, -0.0, +0.0}, + y: []float32{1.0, float32(math.NaN()), math.E, -0.0, +0.0}, + opts: []cmp.Option{EquateNaNs()}, + wantEqual: true, + reason: "equal because EquateNaNs operates on float32", + }, { + label: "EquateApprox+EquateNaNs", + x: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1), 1.01, 5001}, + y: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1), 1.02, 5002}, + opts: []cmp.Option{ + EquateNaNs(), + EquateApprox(0.01, 0), + }, + wantEqual: true, + reason: "equal because EquateNaNs and EquateApprox compose together", + }, { + label: "EquateApprox+EquateNaNs", + x: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.01, 5001}, + y: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.02, 5002}, + opts: []cmp.Option{ + EquateNaNs(), + EquateApprox(0.01, 0), + }, + wantEqual: false, + reason: "not equal because EquateApprox and EquateNaNs do not apply on a named type", + }, { + label: "EquateApprox+EquateNaNs+Transform", + x: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.01, 5001}, + y: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.02, 5002}, + opts: []cmp.Option{ + cmp.Transformer("", func(x MyFloat) float64 { return float64(x) }), + EquateNaNs(), + EquateApprox(0.01, 0), + }, + wantEqual: true, + reason: "equal because named type is transformed to float64", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(0)}, + wantEqual: true, + reason: "equal because times are identical", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: true, + reason: "equal because time is exactly at the allowed margin", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: true, + reason: "equal because time is exactly at the allowed margin (negative)", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3*time.Second - 1)}, + wantEqual: false, + reason: "not equal because time is outside allowed margin", + }, { + label: "EquateApproxTime", + x: time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2009, 11, 10, 23, 0, 3, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3*time.Second - 1)}, + wantEqual: false, + reason: "not equal because time is outside allowed margin (negative)", + }, { + label: "EquateApproxTime", + x: time.Time{}, + y: time.Time{}, + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: true, + reason: "equal because both times are zero", + }, { + label: "EquateApproxTime", + x: time.Time{}, + y: time.Time{}.Add(1), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: false, + reason: "not equal because zero time is always not equal not non-zero", + }, { + label: "EquateApproxTime", + x: time.Time{}.Add(1), + y: time.Time{}, + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: false, + reason: "not equal because zero time is always not equal not non-zero", + }, { + label: "EquateApproxTime", + x: time.Date(2409, 11, 10, 23, 0, 0, 0, time.UTC), + y: time.Date(2000, 11, 10, 23, 0, 3, 0, time.UTC), + opts: []cmp.Option{EquateApproxTime(3 * time.Second)}, + wantEqual: false, + reason: "time difference overflows time.Duration", + }, { + label: "EquateErrors", + x: nil, + y: nil, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "nil values are equal", + }, { + label: "EquateErrors", + x: errors.New("EOF"), + y: io.EOF, + opts: []cmp.Option{EquateErrors()}, + wantEqual: false, + reason: "user-defined EOF is not exactly equal", + }, { + label: "EquateErrors", + x: xerrors.Errorf("wrapped: %w", io.EOF), + y: io.EOF, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "wrapped io.EOF is equal according to errors.Is", + }, { + label: "EquateErrors", + x: xerrors.Errorf("wrapped: %w", io.EOF), + y: io.EOF, + wantEqual: false, + reason: "wrapped io.EOF is not equal without EquateErrors option", + }, { + label: "EquateErrors", + x: io.EOF, + y: io.EOF, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "sentinel errors are equal", + }, { + label: "EquateErrors", + x: io.EOF, + y: AnyError, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "AnyError is equal to any non-nil error", + }, { + label: "EquateErrors", + x: io.EOF, + y: AnyError, + wantEqual: false, + reason: "AnyError is not equal to any non-nil error without EquateErrors option", + }, { + label: "EquateErrors", + x: nil, + y: AnyError, + opts: []cmp.Option{EquateErrors()}, + wantEqual: false, + reason: "AnyError is not equal to nil value", + }, { + label: "EquateErrors", + x: nil, + y: nil, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "nil values are equal", + }, { + label: "EquateErrors", + x: errors.New("EOF"), + y: io.EOF, + opts: []cmp.Option{EquateErrors()}, + wantEqual: false, + reason: "user-defined EOF is not exactly equal", + }, { + label: "EquateErrors", + x: xerrors.Errorf("wrapped: %w", io.EOF), + y: io.EOF, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "wrapped io.EOF is equal according to errors.Is", + }, { + label: "EquateErrors", + x: xerrors.Errorf("wrapped: %w", io.EOF), + y: io.EOF, + wantEqual: false, + reason: "wrapped io.EOF is not equal without EquateErrors option", + }, { + label: "EquateErrors", + x: io.EOF, + y: io.EOF, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "sentinel errors are equal", + }, { + label: "EquateErrors", + x: io.EOF, + y: AnyError, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "AnyError is equal to any non-nil error", + }, { + label: "EquateErrors", + x: io.EOF, + y: AnyError, + wantEqual: false, + reason: "AnyError is not equal to any non-nil error without EquateErrors option", + }, { + label: "EquateErrors", + x: nil, + y: AnyError, + opts: []cmp.Option{EquateErrors()}, + wantEqual: false, + reason: "AnyError is not equal to nil value", + }, { + label: "EquateErrors", + x: struct{ E error }{nil}, + y: struct{ E error }{nil}, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "nil values are equal", + }, { + label: "EquateErrors", + x: struct{ E error }{errors.New("EOF")}, + y: struct{ E error }{io.EOF}, + opts: []cmp.Option{EquateErrors()}, + wantEqual: false, + reason: "user-defined EOF is not exactly equal", + }, { + label: "EquateErrors", + x: struct{ E error }{xerrors.Errorf("wrapped: %w", io.EOF)}, + y: struct{ E error }{io.EOF}, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "wrapped io.EOF is equal according to errors.Is", + }, { + label: "EquateErrors", + x: struct{ E error }{xerrors.Errorf("wrapped: %w", io.EOF)}, + y: struct{ E error }{io.EOF}, + wantEqual: false, + reason: "wrapped io.EOF is not equal without EquateErrors option", + }, { + label: "EquateErrors", + x: struct{ E error }{io.EOF}, + y: struct{ E error }{io.EOF}, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "sentinel errors are equal", + }, { + label: "EquateErrors", + x: struct{ E error }{io.EOF}, + y: struct{ E error }{AnyError}, + opts: []cmp.Option{EquateErrors()}, + wantEqual: true, + reason: "AnyError is equal to any non-nil error", + }, { + label: "EquateErrors", + x: struct{ E error }{io.EOF}, + y: struct{ E error }{AnyError}, + wantEqual: false, + reason: "AnyError is not equal to any non-nil error without EquateErrors option", + }, { + label: "EquateErrors", + x: struct{ E error }{nil}, + y: struct{ E error }{AnyError}, + opts: []cmp.Option{EquateErrors()}, + wantEqual: false, + reason: "AnyError is not equal to nil value", + }, { + label: "IgnoreFields", + x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, + y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, + wantEqual: false, + reason: "not equal because values do not match in deeply embedded field", + }, { + label: "IgnoreFields", + x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, + y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, + opts: []cmp.Option{IgnoreFields(Bar1{}, "Alpha")}, + wantEqual: true, + reason: "equal because IgnoreField ignores deeply embedded field: Alpha", + }, { + label: "IgnoreFields", + x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, + y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, + opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo1.Alpha")}, + wantEqual: true, + reason: "equal because IgnoreField ignores deeply embedded field: Foo1.Alpha", + }, { + label: "IgnoreFields", + x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, + y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, + opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo2.Alpha")}, + wantEqual: true, + reason: "equal because IgnoreField ignores deeply embedded field: Foo2.Alpha", + }, { + label: "IgnoreFields", + x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, + y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, + opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo3.Alpha")}, + wantEqual: true, + reason: "equal because IgnoreField ignores deeply embedded field: Foo3.Alpha", + }, { + label: "IgnoreFields", + x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, + y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, + opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo3.Foo2.Alpha")}, + wantEqual: true, + reason: "equal because IgnoreField ignores deeply embedded field: Foo3.Foo2.Alpha", + }, { + label: "IgnoreFields", + x: createBar3X(), + y: createBar3Y(), + wantEqual: false, + reason: "not equal because many deeply nested or embedded fields differ", + }, { + label: "IgnoreFields", + x: createBar3X(), + y: createBar3Y(), + opts: []cmp.Option{IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Foo3", "Alpha")}, + wantEqual: true, + reason: "equal because IgnoreFields ignores fields at the highest levels", + }, { + label: "IgnoreFields", + x: createBar3X(), + y: createBar3Y(), + opts: []cmp.Option{ + IgnoreFields(Bar3{}, + "Bar1.Foo3.Bravo", + "Bravo.Bar1.Foo3.Foo2.Foo1.Charlie", + "Bravo.Foo3.Foo2.Foo1.Bravo", + "Bravo.Bravo", + "Delta.Echo.Charlie", + "Foo3.Foo2.Foo1.Alpha", + "Alpha", + ), + }, + wantEqual: true, + reason: "equal because IgnoreFields ignores fields using fully-qualified field", + }, { + label: "IgnoreFields", + x: createBar3X(), + y: createBar3Y(), + opts: []cmp.Option{ + IgnoreFields(Bar3{}, + "Bar1.Foo3.Bravo", + "Bravo.Foo3.Foo2.Foo1.Bravo", + "Bravo.Bravo", + "Delta.Echo.Charlie", + "Foo3.Foo2.Foo1.Alpha", + "Alpha", + ), + }, + wantEqual: false, + reason: "not equal because one fully-qualified field is not ignored: Bravo.Bar1.Foo3.Foo2.Foo1.Charlie", + }, { + label: "IgnoreFields", + x: createBar3X(), + y: createBar3Y(), + opts: []cmp.Option{IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Alpha")}, + wantEqual: false, + reason: "not equal because highest-level field is not ignored: Foo3", + }, { + label: "IgnoreFields", + x: ParentStruct{ + privateStruct: &privateStruct{private: 1}, + PublicStruct: &PublicStruct{private: 2}, + private: 3, + }, + y: ParentStruct{ + privateStruct: &privateStruct{private: 10}, + PublicStruct: &PublicStruct{private: 20}, + private: 30, + }, + opts: []cmp.Option{cmp.AllowUnexported(ParentStruct{}, PublicStruct{}, privateStruct{})}, + wantEqual: false, + reason: "not equal because unexported fields mismatch", + }, { + label: "IgnoreFields", + x: ParentStruct{ + privateStruct: &privateStruct{private: 1}, + PublicStruct: &PublicStruct{private: 2}, + private: 3, + }, + y: ParentStruct{ + privateStruct: &privateStruct{private: 10}, + PublicStruct: &PublicStruct{private: 20}, + private: 30, + }, + opts: []cmp.Option{ + cmp.AllowUnexported(ParentStruct{}, PublicStruct{}, privateStruct{}), + IgnoreFields(ParentStruct{}, "PublicStruct.private", "privateStruct.private", "private"), + }, + wantEqual: true, + reason: "equal because mismatching unexported fields are ignored", + }, { + label: "IgnoreTypes", + x: []interface{}{5, "same"}, + y: []interface{}{6, "same"}, + wantEqual: false, + reason: "not equal because 5 != 6", + }, { + label: "IgnoreTypes", + x: []interface{}{5, "same"}, + y: []interface{}{6, "same"}, + opts: []cmp.Option{IgnoreTypes(0)}, + wantEqual: true, + reason: "equal because ints are ignored", + }, { + label: "IgnoreTypes+IgnoreInterfaces", + x: []interface{}{5, "same", new(bytes.Buffer)}, + y: []interface{}{6, "same", new(bytes.Buffer)}, + opts: []cmp.Option{IgnoreTypes(0)}, + wantPanic: true, + reason: "panics because bytes.Buffer has unexported fields", + }, { + label: "IgnoreTypes+IgnoreInterfaces", + x: []interface{}{5, "same", new(bytes.Buffer)}, + y: []interface{}{6, "diff", new(bytes.Buffer)}, + opts: []cmp.Option{ + IgnoreTypes(0, ""), + IgnoreInterfaces(struct{ io.Reader }{}), + }, + wantEqual: true, + reason: "equal because bytes.Buffer is ignored by match on interface type", + }, { + label: "IgnoreTypes+IgnoreInterfaces", + x: []interface{}{5, "same", new(bytes.Buffer)}, + y: []interface{}{6, "same", new(bytes.Buffer)}, + opts: []cmp.Option{ + IgnoreTypes(0, ""), + IgnoreInterfaces(struct { + io.Reader + io.Writer + fmt.Stringer + }{}), + }, + wantEqual: true, + reason: "equal because bytes.Buffer is ignored by match on multiple interface types", + }, { + label: "IgnoreInterfaces", + x: struct{ mu sync.Mutex }{}, + y: struct{ mu sync.Mutex }{}, + wantPanic: true, + reason: "panics because sync.Mutex has unexported fields", + }, { + label: "IgnoreInterfaces", + x: struct{ mu sync.Mutex }{}, + y: struct{ mu sync.Mutex }{}, + opts: []cmp.Option{IgnoreInterfaces(struct{ sync.Locker }{})}, + wantEqual: true, + reason: "equal because IgnoreInterfaces applies on values (with pointer receiver)", + }, { + label: "IgnoreInterfaces", + x: struct{ mu *sync.Mutex }{}, + y: struct{ mu *sync.Mutex }{}, + opts: []cmp.Option{IgnoreInterfaces(struct{ sync.Locker }{})}, + wantEqual: true, + reason: "equal because IgnoreInterfaces applies on pointers", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2}, + y: ParentStruct{Public: 1, private: -2}, + opts: []cmp.Option{cmp.AllowUnexported(ParentStruct{})}, + wantEqual: false, + reason: "not equal because ParentStruct.private differs with AllowUnexported", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2}, + y: ParentStruct{Public: 1, private: -2}, + opts: []cmp.Option{IgnoreUnexported(ParentStruct{})}, + wantEqual: true, + reason: "equal because IgnoreUnexported ignored ParentStruct.private", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, + opts: []cmp.Option{ + cmp.AllowUnexported(PublicStruct{}), + IgnoreUnexported(ParentStruct{}), + }, + wantEqual: true, + reason: "equal because ParentStruct.private is ignored", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: -4}}, + opts: []cmp.Option{ + cmp.AllowUnexported(PublicStruct{}), + IgnoreUnexported(ParentStruct{}), + }, + wantEqual: false, + reason: "not equal because ParentStruct.PublicStruct.private differs and not ignored by IgnoreUnexported(ParentStruct{})", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: -4}}, + opts: []cmp.Option{ + IgnoreUnexported(ParentStruct{}, PublicStruct{}), + }, + wantEqual: true, + reason: "equal because both ParentStruct.PublicStruct and ParentStruct.PublicStruct.private are ignored", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}}, + opts: []cmp.Option{ + cmp.AllowUnexported(privateStruct{}, PublicStruct{}, ParentStruct{}), + }, + wantEqual: false, + reason: "not equal since ParentStruct.privateStruct differs", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}}, + opts: []cmp.Option{ + cmp.AllowUnexported(privateStruct{}, PublicStruct{}), + IgnoreUnexported(ParentStruct{}), + }, + wantEqual: true, + reason: "equal because ParentStruct.privateStruct ignored by IgnoreUnexported(ParentStruct{})", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: -4}}, + opts: []cmp.Option{ + cmp.AllowUnexported(PublicStruct{}, ParentStruct{}), + IgnoreUnexported(privateStruct{}), + }, + wantEqual: true, + reason: "equal because privateStruct.private ignored by IgnoreUnexported(privateStruct{})", + }, { + label: "IgnoreUnexported", + x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, + y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}}, + opts: []cmp.Option{ + cmp.AllowUnexported(PublicStruct{}, ParentStruct{}), + IgnoreUnexported(privateStruct{}), + }, + wantEqual: false, + reason: "not equal because privateStruct.Public differs and not ignored by IgnoreUnexported(privateStruct{})", + }, { + label: "IgnoreFields+IgnoreTypes+IgnoreUnexported", + x: &Everything{ + MyInt: 5, + MyFloat: 3.3, + MyTime: MyTime{time.Now()}, + Bar3: *createBar3X(), + ParentStruct: ParentStruct{ + Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}, + }, + }, + y: &Everything{ + MyInt: -5, + MyFloat: 3.3, + MyTime: MyTime{time.Now()}, + Bar3: *createBar3Y(), + ParentStruct: ParentStruct{ + Public: 1, private: -2, PublicStruct: &PublicStruct{Public: -3, private: -4}, + }, + }, + opts: []cmp.Option{ + IgnoreFields(Everything{}, "MyTime", "Bar3.Foo3"), + IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Alpha"), + IgnoreTypes(MyInt(0), PublicStruct{}), + IgnoreUnexported(ParentStruct{}), + }, + wantEqual: true, + reason: "equal because all Ignore options can be composed together", + }, { + label: "IgnoreSliceElements", + x: []int{1, 0, 2, 3, 0, 4, 0, 0}, + y: []int{0, 0, 0, 0, 1, 2, 3, 4}, + opts: []cmp.Option{ + IgnoreSliceElements(func(v int) bool { return v == 0 }), + }, + wantEqual: true, + reason: "equal because zero elements are ignored", + }, { + label: "IgnoreSliceElements", + x: []MyInt{1, 0, 2, 3, 0, 4, 0, 0}, + y: []MyInt{0, 0, 0, 0, 1, 2, 3, 4}, + opts: []cmp.Option{ + IgnoreSliceElements(func(v int) bool { return v == 0 }), + }, + wantEqual: false, + reason: "not equal because MyInt is not assignable to int", + }, { + label: "IgnoreSliceElements", + x: MyInts{1, 0, 2, 3, 0, 4, 0, 0}, + y: MyInts{0, 0, 0, 0, 1, 2, 3, 4}, + opts: []cmp.Option{ + IgnoreSliceElements(func(v int) bool { return v == 0 }), + }, + wantEqual: true, + reason: "equal because the element type of MyInts is assignable to int", + }, { + label: "IgnoreSliceElements+EquateEmpty", + x: []MyInt{}, + y: []MyInt{0, 0, 0, 0}, + opts: []cmp.Option{ + IgnoreSliceElements(func(v int) bool { return v == 0 }), + EquateEmpty(), + }, + wantEqual: false, + reason: "not equal because ignored elements does not imply empty slice", + }, { + label: "IgnoreMapEntries", + x: map[string]int{"one": 1, "TWO": 2, "three": 3, "FIVE": 5}, + y: map[string]int{"one": 1, "three": 3, "TEN": 10}, + opts: []cmp.Option{ + IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), + }, + wantEqual: true, + reason: "equal because uppercase keys are ignored", + }, { + label: "IgnoreMapEntries", + x: map[MyString]int{"one": 1, "TWO": 2, "three": 3, "FIVE": 5}, + y: map[MyString]int{"one": 1, "three": 3, "TEN": 10}, + opts: []cmp.Option{ + IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), + }, + wantEqual: false, + reason: "not equal because MyString is not assignable to string", + }, { + label: "IgnoreMapEntries", + x: map[string]MyInt{"one": 1, "TWO": 2, "three": 3, "FIVE": 5}, + y: map[string]MyInt{"one": 1, "three": 3, "TEN": 10}, + opts: []cmp.Option{ + IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), + }, + wantEqual: false, + reason: "not equal because MyInt is not assignable to int", + }, { + label: "IgnoreMapEntries+EquateEmpty", + x: map[string]MyInt{"ONE": 1, "TWO": 2, "THREE": 3}, + y: nil, + opts: []cmp.Option{ + IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), + EquateEmpty(), + }, + wantEqual: false, + reason: "not equal because ignored entries does not imply empty map", + }, { + label: "AcyclicTransformer", + x: "a\nb\nc\nd", + y: "a\nb\nd\nd", + opts: []cmp.Option{ + AcyclicTransformer("", func(s string) []string { return strings.Split(s, "\n") }), + }, + wantEqual: false, + reason: "not equal because 3rd line differs, but should not recurse infinitely", + }, { + label: "AcyclicTransformer", + x: []string{"foo", "Bar", "BAZ"}, + y: []string{"Foo", "BAR", "baz"}, + opts: []cmp.Option{ + AcyclicTransformer("", strings.ToUpper), + }, + wantEqual: true, + reason: "equal because of strings.ToUpper; AcyclicTransformer unnecessary, but check this still works", + }, { + label: "AcyclicTransformer", + x: "this is a sentence", + y: "this is a sentence", + opts: []cmp.Option{ + AcyclicTransformer("", strings.Fields), + }, + wantEqual: true, + reason: "equal because acyclic transformer splits on any contiguous whitespace", + }} + + for _, tt := range tests { + t.Run(tt.label, func(t *testing.T) { + var gotEqual bool + var gotPanic string + func() { + defer func() { + if ex := recover(); ex != nil { + gotPanic = fmt.Sprint(ex) + } + }() + gotEqual = cmp.Equal(tt.x, tt.y, tt.opts...) + }() + switch { + case tt.reason == "": + t.Errorf("reason must be provided") + case gotPanic == "" && tt.wantPanic: + t.Errorf("expected Equal panic\nreason: %s", tt.reason) + case gotPanic != "" && !tt.wantPanic: + t.Errorf("unexpected Equal panic: got %v\nreason: %v", gotPanic, tt.reason) + case gotEqual != tt.wantEqual: + t.Errorf("Equal = %v, want %v\nreason: %v", gotEqual, tt.wantEqual, tt.reason) + } + }) + } +} + +func TestPanic(t *testing.T) { + args := func(x ...interface{}) []interface{} { return x } + tests := []struct { + label string // Test name + fnc interface{} // Option function to call + args []interface{} // Arguments to pass in + wantPanic string // Expected panic message + reason string // The reason for the expected outcome + }{{ + label: "EquateApprox", + fnc: EquateApprox, + args: args(0.0, 0.0), + reason: "zero margin and fraction is equivalent to exact equality", + }, { + label: "EquateApprox", + fnc: EquateApprox, + args: args(-0.1, 0.0), + wantPanic: "margin or fraction must be a non-negative number", + reason: "negative inputs are invalid", + }, { + label: "EquateApprox", + fnc: EquateApprox, + args: args(0.0, -0.1), + wantPanic: "margin or fraction must be a non-negative number", + reason: "negative inputs are invalid", + }, { + label: "EquateApprox", + fnc: EquateApprox, + args: args(math.NaN(), 0.0), + wantPanic: "margin or fraction must be a non-negative number", + reason: "NaN inputs are invalid", + }, { + label: "EquateApprox", + fnc: EquateApprox, + args: args(1.0, 0.0), + reason: "fraction of 1.0 or greater is valid", + }, { + label: "EquateApprox", + fnc: EquateApprox, + args: args(0.0, math.Inf(+1)), + reason: "margin of infinity is valid", + }, { + label: "EquateApproxTime", + fnc: EquateApproxTime, + args: args(time.Duration(-1)), + wantPanic: "margin must be a non-negative number", + reason: "negative duration is invalid", + }, { + label: "SortSlices", + fnc: SortSlices, + args: args(strings.Compare), + wantPanic: "invalid less function", + reason: "func(x, y string) int is wrong signature for less", + }, { + label: "SortSlices", + fnc: SortSlices, + args: args((func(_, _ int) bool)(nil)), + wantPanic: "invalid less function", + reason: "nil value is not valid", + }, { + label: "SortMaps", + fnc: SortMaps, + args: args(strings.Compare), + wantPanic: "invalid less function", + reason: "func(x, y string) int is wrong signature for less", + }, { + label: "SortMaps", + fnc: SortMaps, + args: args((func(_, _ int) bool)(nil)), + wantPanic: "invalid less function", + reason: "nil value is not valid", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, ""), + wantPanic: "name must not be empty", + reason: "empty selector is invalid", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, "."), + wantPanic: "name must not be empty", + reason: "single dot selector is invalid", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, ".Alpha"), + reason: "dot-prefix is okay since Foo1.Alpha reads naturally", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, "Alpha."), + wantPanic: "name must not be empty", + reason: "dot-suffix is invalid", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, "Alpha "), + wantPanic: "does not exist", + reason: "identifiers must not have spaces", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, "Zulu"), + wantPanic: "does not exist", + reason: "name of non-existent field is invalid", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(Foo1{}, "Alpha.NoExist"), + wantPanic: "must be a struct", + reason: "cannot select into a non-struct", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(&Foo1{}, "Alpha"), + wantPanic: "must be a non-pointer struct", + reason: "the type must be a struct (not pointer to a struct)", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(struct{ privateStruct }{}, "privateStruct"), + reason: "privateStruct field permitted since it is the default name of the embedded type", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(struct{ privateStruct }{}, "Public"), + reason: "Public field permitted since it is a forwarded field that is exported", + }, { + label: "IgnoreFields", + fnc: IgnoreFields, + args: args(struct{ privateStruct }{}, "private"), + wantPanic: "does not exist", + reason: "private field not permitted since it is a forwarded field that is unexported", + }, { + label: "IgnoreTypes", + fnc: IgnoreTypes, + reason: "empty input is valid", + }, { + label: "IgnoreTypes", + fnc: IgnoreTypes, + args: args(nil), + wantPanic: "cannot determine type", + reason: "input must not be nil value", + }, { + label: "IgnoreTypes", + fnc: IgnoreTypes, + args: args(0, 0, 0), + reason: "duplicate inputs of the same type is valid", + }, { + label: "IgnoreInterfaces", + fnc: IgnoreInterfaces, + args: args(nil), + wantPanic: "input must be an anonymous struct", + reason: "input must not be nil value", + }, { + label: "IgnoreInterfaces", + fnc: IgnoreInterfaces, + args: args(Foo1{}), + wantPanic: "input must be an anonymous struct", + reason: "input must not be a named struct type", + }, { + label: "IgnoreInterfaces", + fnc: IgnoreInterfaces, + args: args(struct{ _ io.Reader }{}), + wantPanic: "struct cannot have named fields", + reason: "input must not have named fields", + }, { + label: "IgnoreInterfaces", + fnc: IgnoreInterfaces, + args: args(struct{ Foo1 }{}), + wantPanic: "embedded field must be an interface type", + reason: "field types must be interfaces", + }, { + label: "IgnoreInterfaces", + fnc: IgnoreInterfaces, + args: args(struct{ EmptyInterface }{}), + wantPanic: "cannot ignore empty interface", + reason: "field types must not be the empty interface", + }, { + label: "IgnoreInterfaces", + fnc: IgnoreInterfaces, + args: args(struct { + io.Reader + io.Writer + io.Closer + io.ReadWriteCloser + }{}), + reason: "multiple interfaces may be specified, even if they overlap", + }, { + label: "IgnoreUnexported", + fnc: IgnoreUnexported, + reason: "empty input is valid", + }, { + label: "IgnoreUnexported", + fnc: IgnoreUnexported, + args: args(nil), + wantPanic: "must be a non-pointer struct", + reason: "input must not be nil value", + }, { + label: "IgnoreUnexported", + fnc: IgnoreUnexported, + args: args(&Foo1{}), + wantPanic: "must be a non-pointer struct", + reason: "input must be a struct type (not a pointer to a struct)", + }, { + label: "IgnoreUnexported", + fnc: IgnoreUnexported, + args: args(Foo1{}, struct{ x, X int }{}), + reason: "input may be named or unnamed structs", + }, { + label: "AcyclicTransformer", + fnc: AcyclicTransformer, + args: args("", "not a func"), + wantPanic: "invalid transformer function", + reason: "AcyclicTransformer has same input requirements as Transformer", + }} + + for _, tt := range tests { + t.Run(tt.label, func(t *testing.T) { + // Prepare function arguments. + vf := reflect.ValueOf(tt.fnc) + var vargs []reflect.Value + for i, arg := range tt.args { + if arg == nil { + tf := vf.Type() + if i == tf.NumIn()-1 && tf.IsVariadic() { + vargs = append(vargs, reflect.Zero(tf.In(i).Elem())) + } else { + vargs = append(vargs, reflect.Zero(tf.In(i))) + } + } else { + vargs = append(vargs, reflect.ValueOf(arg)) + } + } + + // Call the function and capture any panics. + var gotPanic string + func() { + defer func() { + if ex := recover(); ex != nil { + if s, ok := ex.(string); ok { + gotPanic = s + } else { + panic(ex) + } + } + }() + vf.Call(vargs) + }() + + switch { + case tt.reason == "": + t.Errorf("reason must be provided") + case tt.wantPanic == "" && gotPanic != "": + t.Errorf("unexpected panic message: %s\nreason: %s", gotPanic, tt.reason) + case tt.wantPanic != "" && !strings.Contains(gotPanic, tt.wantPanic): + t.Errorf("panic message:\ngot: %s\nwant: %s\nreason: %s", gotPanic, tt.wantPanic, tt.reason) + } + }) + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/xform.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/xform.go new file mode 100644 index 0000000..4eb49d6 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/cmpopts/xform.go @@ -0,0 +1,35 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmpopts + +import ( + "github.com/google/go-cmp/cmp" +) + +type xformFilter struct{ xform cmp.Option } + +func (xf xformFilter) filter(p cmp.Path) bool { + for _, ps := range p { + if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform { + return false + } + } + return true +} + +// AcyclicTransformer returns a Transformer with a filter applied that ensures +// that the transformer cannot be recursively applied upon its own output. +// +// An example use case is a transformer that splits a string by lines: +// AcyclicTransformer("SplitLines", func(s string) []string{ +// return strings.Split(s, "\n") +// }) +// +// Had this been an unfiltered Transformer instead, this would result in an +// infinite cycle converting a string to []string to [][]string and so on. +func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { + xf := xformFilter{cmp.Transformer(name, xformFunc)} + return cmp.FilterPath(xf.filter, xf.xform) +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare.go new file mode 100644 index 0000000..86d0903 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare.go @@ -0,0 +1,682 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cmp determines equality of values. +// +// This package is intended to be a more powerful and safer alternative to +// reflect.DeepEqual for comparing whether two values are semantically equal. +// It is intended to only be used in tests, as performance is not a goal and +// it may panic if it cannot compare the values. Its propensity towards +// panicking means that its unsuitable for production environments where a +// spurious panic may be fatal. +// +// The primary features of cmp are: +// +// • When the default behavior of equality does not suit the needs of the test, +// custom equality functions can override the equality operation. +// For example, an equality function may report floats as equal so long as they +// are within some tolerance of each other. +// +// • Types that have an Equal method may use that method to determine equality. +// This allows package authors to determine the equality operation for the types +// that they define. +// +// • If no custom equality functions are used and no Equal method is defined, +// equality is determined by recursively comparing the primitive kinds on both +// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported +// fields are not compared by default; they result in panics unless suppressed +// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly +// compared using the Exporter option. +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/diff" + "github.com/google/go-cmp/cmp/internal/flags" + "github.com/google/go-cmp/cmp/internal/function" + "github.com/google/go-cmp/cmp/internal/value" +) + +// Equal reports whether x and y are equal by recursively applying the +// following rules in the given order to x and y and all of their sub-values: +// +// • Let S be the set of all Ignore, Transformer, and Comparer options that +// remain after applying all path filters, value filters, and type filters. +// If at least one Ignore exists in S, then the comparison is ignored. +// If the number of Transformer and Comparer options in S is greater than one, +// then Equal panics because it is ambiguous which option to use. +// If S contains a single Transformer, then use that to transform the current +// values and recursively call Equal on the output values. +// If S contains a single Comparer, then use that to compare the current values. +// Otherwise, evaluation proceeds to the next rule. +// +// • If the values have an Equal method of the form "(T) Equal(T) bool" or +// "(T) Equal(I) bool" where T is assignable to I, then use the result of +// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and +// evaluation proceeds to the next rule. +// +// • Lastly, try to compare x and y based on their basic kinds. +// Simple kinds like booleans, integers, floats, complex numbers, strings, and +// channels are compared using the equivalent of the == operator in Go. +// Functions are only equal if they are both nil, otherwise they are unequal. +// +// Structs are equal if recursively calling Equal on all fields report equal. +// If a struct contains unexported fields, Equal panics unless an Ignore option +// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option +// explicitly permits comparing the unexported field. +// +// Slices are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored slice or array elements report equal. +// Empty non-nil slices and nil slices are not equal; to equate empty slices, +// consider using cmpopts.EquateEmpty. +// +// Maps are equal if they are both nil or both non-nil, where recursively +// calling Equal on all non-ignored map entries report equal. +// Map keys are equal according to the == operator. +// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +// Empty non-nil maps and nil maps are not equal; to equate empty maps, +// consider using cmpopts.EquateEmpty. +// +// Pointers and interfaces are equal if they are both nil or both non-nil, +// where they have the same underlying concrete type and recursively +// calling Equal on the underlying values reports equal. +// +// Before recursing into a pointer, slice element, or map, the current path +// is checked to detect whether the address has already been visited. +// If there is a cycle, then the pointed at values are considered equal +// only if both addresses were previously visited in the same path step. +func Equal(x, y interface{}, opts ...Option) bool { + s := newState(opts) + s.compareAny(rootStep(x, y)) + return s.result.Equal() +} + +// Diff returns a human-readable report of the differences between two values: +// y - x. It returns an empty string if and only if Equal returns true for the +// same input values and options. +// +// The output is displayed as a literal in pseudo-Go syntax. +// At the start of each line, a "-" prefix indicates an element removed from x, +// a "+" prefix to indicates an element added from y, and the lack of a prefix +// indicates an element common to both x and y. If possible, the output +// uses fmt.Stringer.String or error.Error methods to produce more humanly +// readable outputs. In such cases, the string is prefixed with either an +// 's' or 'e' character, respectively, to indicate that the method was called. +// +// Do not depend on this output being stable. If you need the ability to +// programmatically interpret the difference, consider using a custom Reporter. +func Diff(x, y interface{}, opts ...Option) string { + s := newState(opts) + + // Optimization: If there are no other reporters, we can optimize for the + // common case where the result is equal (and thus no reported difference). + // This avoids the expensive construction of a difference tree. + if len(s.reporters) == 0 { + s.compareAny(rootStep(x, y)) + if s.result.Equal() { + return "" + } + s.result = diff.Result{} // Reset results + } + + r := new(defaultReporter) + s.reporters = append(s.reporters, reporter{r}) + s.compareAny(rootStep(x, y)) + d := r.String() + if (d == "") != s.result.Equal() { + panic("inconsistent difference and equality results") + } + return d +} + +// rootStep constructs the first path step. If x and y have differing types, +// then they are stored within an empty interface type. +func rootStep(x, y interface{}) PathStep { + vx := reflect.ValueOf(x) + vy := reflect.ValueOf(y) + + // If the inputs are different types, auto-wrap them in an empty interface + // so that they have the same parent type. + var t reflect.Type + if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { + t = reflect.TypeOf((*interface{})(nil)).Elem() + if vx.IsValid() { + vvx := reflect.New(t).Elem() + vvx.Set(vx) + vx = vvx + } + if vy.IsValid() { + vvy := reflect.New(t).Elem() + vvy.Set(vy) + vy = vvy + } + } else { + t = vx.Type() + } + + return &pathStep{t, vx, vy} +} + +type state struct { + // These fields represent the "comparison state". + // Calling statelessCompare must not result in observable changes to these. + result diff.Result // The current result of comparison + curPath Path // The current path in the value tree + curPtrs pointerPath // The current set of visited pointers + reporters []reporter // Optional reporters + + // recChecker checks for infinite cycles applying the same set of + // transformers upon the output of itself. + recChecker recChecker + + // dynChecker triggers pseudo-random checks for option correctness. + // It is safe for statelessCompare to mutate this value. + dynChecker dynChecker + + // These fields, once set by processOption, will not change. + exporters []exporter // List of exporters for structs with unexported fields + opts Options // List of all fundamental and filter options +} + +func newState(opts []Option) *state { + // Always ensure a validator option exists to validate the inputs. + s := &state{opts: Options{validator{}}} + s.curPtrs.Init() + s.processOption(Options(opts)) + return s +} + +func (s *state) processOption(opt Option) { + switch opt := opt.(type) { + case nil: + case Options: + for _, o := range opt { + s.processOption(o) + } + case coreOption: + type filtered interface { + isFiltered() bool + } + if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { + panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) + } + s.opts = append(s.opts, opt) + case exporter: + s.exporters = append(s.exporters, opt) + case reporter: + s.reporters = append(s.reporters, opt) + default: + panic(fmt.Sprintf("unknown option %T", opt)) + } +} + +// statelessCompare compares two values and returns the result. +// This function is stateless in that it does not alter the current result, +// or output to any registered reporters. +func (s *state) statelessCompare(step PathStep) diff.Result { + // We do not save and restore curPath and curPtrs because all of the + // compareX methods should properly push and pop from them. + // It is an implementation bug if the contents of the paths differ from + // when calling this function to when returning from it. + + oldResult, oldReporters := s.result, s.reporters + s.result = diff.Result{} // Reset result + s.reporters = nil // Remove reporters to avoid spurious printouts + s.compareAny(step) + res := s.result + s.result, s.reporters = oldResult, oldReporters + return res +} + +func (s *state) compareAny(step PathStep) { + // Update the path stack. + s.curPath.push(step) + defer s.curPath.pop() + for _, r := range s.reporters { + r.PushStep(step) + defer r.PopStep() + } + s.recChecker.Check(s.curPath) + + // Cycle-detection for slice elements (see NOTE in compareSlice). + t := step.Type() + vx, vy := step.Values() + if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { + px, py := vx.Addr(), vy.Addr() + if eq, visited := s.curPtrs.Push(px, py); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(px, py) + } + + // Rule 1: Check whether an option applies on this node in the value tree. + if s.tryOptions(t, vx, vy) { + return + } + + // Rule 2: Check whether the type has a valid Equal method. + if s.tryMethod(t, vx, vy) { + return + } + + // Rule 3: Compare based on the underlying kind. + switch t.Kind() { + case reflect.Bool: + s.report(vx.Bool() == vy.Bool(), 0) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s.report(vx.Int() == vy.Int(), 0) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + s.report(vx.Uint() == vy.Uint(), 0) + case reflect.Float32, reflect.Float64: + s.report(vx.Float() == vy.Float(), 0) + case reflect.Complex64, reflect.Complex128: + s.report(vx.Complex() == vy.Complex(), 0) + case reflect.String: + s.report(vx.String() == vy.String(), 0) + case reflect.Chan, reflect.UnsafePointer: + s.report(vx.Pointer() == vy.Pointer(), 0) + case reflect.Func: + s.report(vx.IsNil() && vy.IsNil(), 0) + case reflect.Struct: + s.compareStruct(t, vx, vy) + case reflect.Slice, reflect.Array: + s.compareSlice(t, vx, vy) + case reflect.Map: + s.compareMap(t, vx, vy) + case reflect.Ptr: + s.comparePtr(t, vx, vy) + case reflect.Interface: + s.compareInterface(t, vx, vy) + default: + panic(fmt.Sprintf("%v kind not handled", t.Kind())) + } +} + +func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { + // Evaluate all filters and apply the remaining options. + if opt := s.opts.filter(s, t, vx, vy); opt != nil { + opt.apply(s, vx, vy) + return true + } + return false +} + +func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { + // Check if this type even has an Equal method. + m, ok := t.MethodByName("Equal") + if !ok || !function.IsType(m.Type, function.EqualAssignable) { + return false + } + + eq := s.callTTBFunc(m.Func, vx, vy) + s.report(eq, reportByMethod) + return true +} + +func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { + v = sanitizeValue(v, f.Type().In(0)) + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{v})[0] + } + + // Run the function twice and ensure that we get the same results back. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, v) + got := <-c + want := f.Call([]reflect.Value{v})[0] + if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { + // To avoid false-positives with non-reflexive equality operations, + // we sanity check whether a value is equal to itself. + if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { + return want + } + panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) + } + return want +} + +func (s *state) callTTBFunc(f, x, y reflect.Value) bool { + x = sanitizeValue(x, f.Type().In(0)) + y = sanitizeValue(y, f.Type().In(1)) + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{x, y})[0].Bool() + } + + // Swapping the input arguments is sufficient to check that + // f is symmetric and deterministic. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, y, x) + got := <-c + want := f.Call([]reflect.Value{x, y})[0].Bool() + if !got.IsValid() || got.Bool() != want { + panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) + } + return want +} + +func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { + var ret reflect.Value + defer func() { + recover() // Ignore panics, let the other call to f panic instead + c <- ret + }() + ret = f.Call(vs)[0] +} + +// sanitizeValue converts nil interfaces of type T to those of type R, +// assuming that T is assignable to R. +// Otherwise, it returns the input value as is. +func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { + // TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/22143). + if !flags.AtLeastGo110 { + if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { + return reflect.New(t).Elem() + } + } + return v +} + +func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { + var addr bool + var vax, vay reflect.Value // Addressable versions of vx and vy + + var mayForce, mayForceInit bool + step := StructField{&structField{}} + for i := 0; i < t.NumField(); i++ { + step.typ = t.Field(i).Type + step.vx = vx.Field(i) + step.vy = vy.Field(i) + step.name = t.Field(i).Name + step.idx = i + step.unexported = !isExported(step.name) + if step.unexported { + if step.name == "_" { + continue + } + // Defer checking of unexported fields until later to give an + // Ignore a chance to ignore the field. + if !vax.IsValid() || !vay.IsValid() { + // For retrieveUnexportedField to work, the parent struct must + // be addressable. Create a new copy of the values if + // necessary to make them addressable. + addr = vx.CanAddr() || vy.CanAddr() + vax = makeAddressable(vx) + vay = makeAddressable(vy) + } + if !mayForceInit { + for _, xf := range s.exporters { + mayForce = mayForce || xf(t) + } + mayForceInit = true + } + step.mayForce = mayForce + step.paddr = addr + step.pvx = vax + step.pvy = vay + step.field = t.Field(i) + } + s.compareAny(step) + } +} + +func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { + isSlice := t.Kind() == reflect.Slice + if isSlice && (vx.IsNil() || vy.IsNil()) { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer + // since slices represents a list of pointers, rather than a single pointer. + // The pointer checking logic must be handled on a per-element basis + // in compareAny. + // + // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting + // pointer P, a length N, and a capacity C. Supposing each slice element has + // a memory size of M, then the slice is equivalent to the list of pointers: + // [P+i*M for i in range(N)] + // + // For example, v[:0] and v[:1] are slices with the same starting pointer, + // but they are clearly different values. Using the slice pointer alone + // violates the assumption that equal pointers implies equal values. + + step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}} + withIndexes := func(ix, iy int) SliceIndex { + if ix >= 0 { + step.vx, step.xkey = vx.Index(ix), ix + } else { + step.vx, step.xkey = reflect.Value{}, -1 + } + if iy >= 0 { + step.vy, step.ykey = vy.Index(iy), iy + } else { + step.vy, step.ykey = reflect.Value{}, -1 + } + return step + } + + // Ignore options are able to ignore missing elements in a slice. + // However, detecting these reliably requires an optimal differencing + // algorithm, for which diff.Difference is not. + // + // Instead, we first iterate through both slices to detect which elements + // would be ignored if standing alone. The index of non-discarded elements + // are stored in a separate slice, which diffing is then performed on. + var indexesX, indexesY []int + var ignoredX, ignoredY []bool + for ix := 0; ix < vx.Len(); ix++ { + ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0 + if !ignored { + indexesX = append(indexesX, ix) + } + ignoredX = append(ignoredX, ignored) + } + for iy := 0; iy < vy.Len(); iy++ { + ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0 + if !ignored { + indexesY = append(indexesY, iy) + } + ignoredY = append(ignoredY, ignored) + } + + // Compute an edit-script for slices vx and vy (excluding ignored elements). + edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result { + return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy])) + }) + + // Replay the ignore-scripts and the edit-script. + var ix, iy int + for ix < vx.Len() || iy < vy.Len() { + var e diff.EditType + switch { + case ix < len(ignoredX) && ignoredX[ix]: + e = diff.UniqueX + case iy < len(ignoredY) && ignoredY[iy]: + e = diff.UniqueY + default: + e, edits = edits[0], edits[1:] + } + switch e { + case diff.UniqueX: + s.compareAny(withIndexes(ix, -1)) + ix++ + case diff.UniqueY: + s.compareAny(withIndexes(-1, iy)) + iy++ + default: + s.compareAny(withIndexes(ix, iy)) + ix++ + iy++ + } + } +} + +func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // Cycle-detection for maps. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) + + // We combine and sort the two map keys so that we can perform the + // comparisons in a deterministic order. + step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}} + for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { + step.vx = vx.MapIndex(k) + step.vy = vy.MapIndex(k) + step.key = k + if !step.vx.IsValid() && !step.vy.IsValid() { + // It is possible for both vx and vy to be invalid if the + // key contained a NaN value in it. + // + // Even with the ability to retrieve NaN keys in Go 1.12, + // there still isn't a sensible way to compare the values since + // a NaN key may map to multiple unordered values. + // The most reasonable way to compare NaNs would be to compare the + // set of values. However, this is impossible to do efficiently + // since set equality is provably an O(n^2) operation given only + // an Equal function. If we had a Less function or Hash function, + // this could be done in O(n*log(n)) or O(n), respectively. + // + // Rather than adding complex logic to deal with NaNs, make it + // the user's responsibility to compare such obscure maps. + const help = "consider providing a Comparer to compare the map" + panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help)) + } + s.compareAny(step) + } +} + +func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + + // Cycle-detection for pointers. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) + + vx, vy = vx.Elem(), vy.Elem() + s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) +} + +func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), 0) + return + } + vx, vy = vx.Elem(), vy.Elem() + if vx.Type() != vy.Type() { + s.report(false, 0) + return + } + s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}}) +} + +func (s *state) report(eq bool, rf resultFlags) { + if rf&reportByIgnore == 0 { + if eq { + s.result.NumSame++ + rf |= reportEqual + } else { + s.result.NumDiff++ + rf |= reportUnequal + } + } + for _, r := range s.reporters { + r.Report(Result{flags: rf}) + } +} + +// recChecker tracks the state needed to periodically perform checks that +// user provided transformers are not stuck in an infinitely recursive cycle. +type recChecker struct{ next int } + +// Check scans the Path for any recursive transformers and panics when any +// recursive transformers are detected. Note that the presence of a +// recursive Transformer does not necessarily imply an infinite cycle. +// As such, this check only activates after some minimal number of path steps. +func (rc *recChecker) Check(p Path) { + const minLen = 1 << 16 + if rc.next == 0 { + rc.next = minLen + } + if len(p) < rc.next { + return + } + rc.next <<= 1 + + // Check whether the same transformer has appeared at least twice. + var ss []string + m := map[Option]int{} + for _, ps := range p { + if t, ok := ps.(Transform); ok { + t := t.Option() + if m[t] == 1 { // Transformer was used exactly once before + tf := t.(*transformer).fnc.Type() + ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0))) + } + m[t]++ + } + } + if len(ss) > 0 { + const warning = "recursive set of Transformers detected" + const help = "consider using cmpopts.AcyclicTransformer" + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help)) + } +} + +// dynChecker tracks the state needed to periodically perform checks that +// user provided functions are symmetric and deterministic. +// The zero value is safe for immediate use. +type dynChecker struct{ curr, next int } + +// Next increments the state and reports whether a check should be performed. +// +// Checks occur every Nth function call, where N is a triangular number: +// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... +// See https://en.wikipedia.org/wiki/Triangular_number +// +// This sequence ensures that the cost of checks drops significantly as +// the number of functions calls grows larger. +func (dc *dynChecker) Next() bool { + ok := dc.curr == dc.next + if ok { + dc.curr = 0 + dc.next++ + } + dc.curr++ + return ok +} + +// makeAddressable returns a value that is always addressable. +// It returns the input verbatim if it is already addressable, +// otherwise it creates a new value and returns an addressable copy. +func makeAddressable(v reflect.Value) reflect.Value { + if v.CanAddr() { + return v + } + vc := reflect.New(v.Type()).Elem() + vc.Set(v) + return vc +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare_test.go new file mode 100644 index 0000000..f7b1f13 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/compare_test.go @@ -0,0 +1,2900 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp_test + +import ( + "bytes" + "crypto/md5" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "math" + "math/rand" + "reflect" + "regexp" + "sort" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/go-cmp/cmp/internal/flags" + + pb "github.com/google/go-cmp/cmp/internal/testprotos" + ts "github.com/google/go-cmp/cmp/internal/teststructs" + foo1 "github.com/google/go-cmp/cmp/internal/teststructs/foo1" + foo2 "github.com/google/go-cmp/cmp/internal/teststructs/foo2" +) + +func init() { + flags.Deterministic = true +} + +var update = flag.Bool("update", false, "update golden test files") + +const goldenHeaderPrefix = "<<< " +const goldenFooterPrefix = ">>> " + +/// mustParseGolden parses a file as a set of key-value pairs. +// +// The syntax is simple and looks something like: +// +// <<< Key1 +// value1a +// value1b +// >>> Key1 +// <<< Key2 +// value2 +// >>> Key2 +// +// It is the user's responsibility to choose a sufficiently unique key name +// such that it never appears in the body of the value itself. +func mustParseGolden(path string) map[string]string { + b, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + s := string(b) + + out := map[string]string{} + for len(s) > 0 { + // Identify the next header. + i := strings.Index(s, "\n") + len("\n") + header := s[:i] + if !strings.HasPrefix(header, goldenHeaderPrefix) { + panic(fmt.Sprintf("invalid header: %q", header)) + } + + // Locate the next footer. + footer := goldenFooterPrefix + header[len(goldenHeaderPrefix):] + j := strings.Index(s, footer) + if j < 0 { + panic(fmt.Sprintf("missing footer: %q", footer)) + } + + // Store the name and data. + name := header[len(goldenHeaderPrefix) : len(header)-len("\n")] + if _, ok := out[name]; ok { + panic(fmt.Sprintf("duplicate name: %q", name)) + } + out[name] = s[len(header):j] + s = s[j+len(footer):] + } + return out +} +func mustFormatGolden(path string, in []struct{ Name, Data string }) { + var b []byte + for _, v := range in { + b = append(b, goldenHeaderPrefix+v.Name+"\n"...) + b = append(b, v.Data...) + b = append(b, goldenFooterPrefix+v.Name+"\n"...) + } + if err := ioutil.WriteFile(path, b, 0664); err != nil { + panic(err) + } +} + +var now = time.Date(2009, time.November, 10, 23, 00, 00, 00, time.UTC) + +func newInt(n int) *int { return &n } + +type Stringer string + +func newStringer(s string) fmt.Stringer { return (*Stringer)(&s) } +func (s Stringer) String() string { return string(s) } + +type test struct { + label string // Test name + x, y interface{} // Input values to compare + opts []cmp.Option // Input options + wantEqual bool // Whether any difference is expected + wantPanic string // Sub-string of an expected panic message + reason string // The reason for the expected outcome +} + +func TestDiff(t *testing.T) { + var tests []test + tests = append(tests, comparerTests()...) + tests = append(tests, transformerTests()...) + tests = append(tests, reporterTests()...) + tests = append(tests, embeddedTests()...) + tests = append(tests, methodTests()...) + tests = append(tests, cycleTests()...) + tests = append(tests, project1Tests()...) + tests = append(tests, project2Tests()...) + tests = append(tests, project3Tests()...) + tests = append(tests, project4Tests()...) + + const goldenFile = "testdata/diffs" + gotDiffs := []struct{ Name, Data string }{} + wantDiffs := mustParseGolden(goldenFile) + for _, tt := range tests { + tt := tt + t.Run(tt.label, func(t *testing.T) { + if !*update { + t.Parallel() + } + var gotDiff, gotPanic string + func() { + defer func() { + if ex := recover(); ex != nil { + if s, ok := ex.(string); ok { + gotPanic = s + } else { + panic(ex) + } + } + }() + gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...) + }() + + switch { + case strings.Contains(t.Name(), "#"): + panic("unique test name must be provided") + case tt.reason == "": + panic("reason must be provided") + case tt.wantPanic == "": + if gotPanic != "" { + t.Fatalf("unexpected panic message: %s\nreason: %v", gotPanic, tt.reason) + } + if *update { + if gotDiff != "" { + gotDiffs = append(gotDiffs, struct{ Name, Data string }{t.Name(), gotDiff}) + } + } else { + wantDiff := wantDiffs[t.Name()] + if diff := cmp.Diff(wantDiff, gotDiff); diff != "" { + t.Fatalf("Diff:\ngot:\n%s\nwant:\n%s\ndiff: (-want +got)\n%s\nreason: %v", gotDiff, wantDiff, diff, tt.reason) + } + } + gotEqual := gotDiff == "" + if gotEqual != tt.wantEqual { + t.Fatalf("Equal = %v, want %v\nreason: %v", gotEqual, tt.wantEqual, tt.reason) + } + default: + if !strings.Contains(gotPanic, tt.wantPanic) { + t.Fatalf("panic message:\ngot: %s\nwant: %s\nreason: %v", gotPanic, tt.wantPanic, tt.reason) + } + } + }) + } + + if *update { + mustFormatGolden(goldenFile, gotDiffs) + } +} + +func comparerTests() []test { + const label = "Comparer" + + type Iface1 interface { + Method() + } + type Iface2 interface { + Method() + } + + type tarHeader struct { + Name string + Mode int64 + Uid int + Gid int + Size int64 + ModTime time.Time + Typeflag byte + Linkname string + Uname string + Gname string + Devmajor int64 + Devminor int64 + AccessTime time.Time + ChangeTime time.Time + Xattrs map[string]string + } + + type namedWithUnexported struct { + unexported string + } + + makeTarHeaders := func(tf byte) (hs []tarHeader) { + for i := 0; i < 5; i++ { + hs = append(hs, tarHeader{ + Name: fmt.Sprintf("some/dummy/test/file%d", i), + Mode: 0664, Uid: i * 1000, Gid: i * 1000, Size: 1 << uint(i), + ModTime: now.Add(time.Duration(i) * time.Hour), + Uname: "user", Gname: "group", + Typeflag: tf, + }) + } + return hs + } + + return []test{{ + label: label + "/Nil", + x: nil, + y: nil, + wantEqual: true, + reason: "nils are equal", + }, { + label: label + "/Integer", + x: 1, + y: 1, + wantEqual: true, + reason: "identical integers are equal", + }, { + label: label + "/UnfilteredIgnore", + x: 1, + y: 1, + opts: []cmp.Option{cmp.Ignore()}, + wantPanic: "cannot use an unfiltered option", + reason: "unfiltered options are functionally useless", + }, { + label: label + "/UnfilteredCompare", + x: 1, + y: 1, + opts: []cmp.Option{cmp.Comparer(func(_, _ interface{}) bool { return true })}, + wantPanic: "cannot use an unfiltered option", + reason: "unfiltered options are functionally useless", + }, { + label: label + "/UnfilteredTransform", + x: 1, + y: 1, + opts: []cmp.Option{cmp.Transformer("λ", func(x interface{}) interface{} { return x })}, + wantPanic: "cannot use an unfiltered option", + reason: "unfiltered options are functionally useless", + }, { + label: label + "/AmbiguousOptions", + x: 1, + y: 1, + opts: []cmp.Option{ + cmp.Comparer(func(x, y int) bool { return true }), + cmp.Transformer("λ", func(x int) float64 { return float64(x) }), + }, + wantPanic: "ambiguous set of applicable options", + reason: "both options apply on int, leading to ambiguity", + }, { + label: label + "/IgnorePrecedence", + x: 1, + y: 1, + opts: []cmp.Option{ + cmp.FilterPath(func(p cmp.Path) bool { + return len(p) > 0 && p[len(p)-1].Type().Kind() == reflect.Int + }, cmp.Options{cmp.Ignore(), cmp.Ignore(), cmp.Ignore()}), + cmp.Comparer(func(x, y int) bool { return true }), + cmp.Transformer("λ", func(x int) float64 { return float64(x) }), + }, + wantEqual: true, + reason: "ignore takes precedence over other options", + }, { + label: label + "/UnknownOption", + opts: []cmp.Option{struct{ cmp.Option }{}}, + wantPanic: "unknown option", + reason: "use of unknown option should panic", + }, { + label: label + "/StructEqual", + x: struct{ A, B, C int }{1, 2, 3}, + y: struct{ A, B, C int }{1, 2, 3}, + wantEqual: true, + reason: "struct comparison with all equal fields", + }, { + label: label + "/StructInequal", + x: struct{ A, B, C int }{1, 2, 3}, + y: struct{ A, B, C int }{1, 2, 4}, + wantEqual: false, + reason: "struct comparison with inequal C field", + }, { + label: label + "/StructUnexported", + x: struct{ a, b, c int }{1, 2, 3}, + y: struct{ a, b, c int }{1, 2, 4}, + wantPanic: "cannot handle unexported field", + reason: "unexported fields result in a panic by default", + }, { + label: label + "/PointerStructEqual", + x: &struct{ A *int }{newInt(4)}, + y: &struct{ A *int }{newInt(4)}, + wantEqual: true, + reason: "comparison of pointer to struct with equal A field", + }, { + label: label + "/PointerStructInequal", + x: &struct{ A *int }{newInt(4)}, + y: &struct{ A *int }{newInt(5)}, + wantEqual: false, + reason: "comparison of pointer to struct with inequal A field", + }, { + label: label + "/PointerStructTrueComparer", + x: &struct{ A *int }{newInt(4)}, + y: &struct{ A *int }{newInt(5)}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y int) bool { return true }), + }, + wantEqual: true, + reason: "comparison of pointer to struct with inequal A field, but treated as equal with always equal comparer", + }, { + label: label + "/PointerStructNonNilComparer", + x: &struct{ A *int }{newInt(4)}, + y: &struct{ A *int }{newInt(5)}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }), + }, + wantEqual: true, + reason: "comparison of pointer to struct with inequal A field, but treated as equal with comparer checking pointers for nilness", + }, { + label: label + "/StructNestedPointerEqual", + x: &struct{ R *bytes.Buffer }{}, + y: &struct{ R *bytes.Buffer }{}, + wantEqual: true, + reason: "equal since both pointers in R field are nil", + }, { + label: label + "/StructNestedPointerInequal", + x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)}, + y: &struct{ R *bytes.Buffer }{}, + wantEqual: false, + reason: "inequal since R field is inequal", + }, { + label: label + "/StructNestedPointerTrueComparer", + x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)}, + y: &struct{ R *bytes.Buffer }{}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y io.Reader) bool { return true }), + }, + wantEqual: true, + reason: "equal despite inequal R field values since the comparer always reports true", + }, { + label: label + "/StructNestedValueUnexportedPanic1", + x: &struct{ R bytes.Buffer }{}, + y: &struct{ R bytes.Buffer }{}, + wantPanic: "cannot handle unexported field", + reason: "bytes.Buffer contains unexported fields", + }, { + label: label + "/StructNestedValueUnexportedPanic2", + x: &struct{ R bytes.Buffer }{}, + y: &struct{ R bytes.Buffer }{}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y io.Reader) bool { return true }), + }, + wantPanic: "cannot handle unexported field", + reason: "bytes.Buffer value does not implement io.Reader", + }, { + label: label + "/StructNestedValueEqual", + x: &struct{ R bytes.Buffer }{}, + y: &struct{ R bytes.Buffer }{}, + opts: []cmp.Option{ + cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }), + cmp.Comparer(func(x, y io.Reader) bool { return true }), + }, + wantEqual: true, + reason: "bytes.Buffer pointer due to shallow copy does implement io.Reader", + }, { + label: label + "/RegexpUnexportedPanic", + x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, + y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, + wantPanic: "cannot handle unexported field", + reason: "regexp.Regexp contains unexported fields", + }, { + label: label + "/RegexpEqual", + x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, + y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, + opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool { + if x == nil || y == nil { + return x == nil && y == nil + } + return x.String() == y.String() + })}, + wantEqual: true, + reason: "comparer for *regexp.Regexp applied with equal regexp strings", + }, { + label: label + "/RegexpInequal", + x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, + y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*d*")}, + opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool { + if x == nil || y == nil { + return x == nil && y == nil + } + return x.String() == y.String() + })}, + wantEqual: false, + reason: "comparer for *regexp.Regexp applied with inequal regexp strings", + }, { + label: label + "/TriplePointerEqual", + x: func() ***int { + a := 0 + b := &a + c := &b + return &c + }(), + y: func() ***int { + a := 0 + b := &a + c := &b + return &c + }(), + wantEqual: true, + reason: "three layers of pointers to the same value", + }, { + label: label + "/TriplePointerInequal", + x: func() ***int { + a := 0 + b := &a + c := &b + return &c + }(), + y: func() ***int { + a := 1 + b := &a + c := &b + return &c + }(), + wantEqual: false, + reason: "three layers of pointers to different values", + }, { + label: label + "/SliceWithDifferingCapacity", + x: []int{1, 2, 3, 4, 5}[:3], + y: []int{1, 2, 3}, + wantEqual: true, + reason: "elements past the slice length are not compared", + }, { + label: label + "/StringerEqual", + x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")}, + y: struct{ fmt.Stringer }{regexp.MustCompile("hello")}, + opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, + wantEqual: true, + reason: "comparer for fmt.Stringer used to compare differing types with same string", + }, { + label: label + "/StringerInequal", + x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")}, + y: struct{ fmt.Stringer }{regexp.MustCompile("hello2")}, + opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, + wantEqual: false, + reason: "comparer for fmt.Stringer used to compare differing types with different strings", + }, { + label: label + "/DifferingHash", + x: md5.Sum([]byte{'a'}), + y: md5.Sum([]byte{'b'}), + wantEqual: false, + reason: "hash differs", + }, { + label: label + "/NilStringer", + x: new(fmt.Stringer), + y: nil, + wantEqual: false, + reason: "by default differing types are always inequal", + }, { + label: label + "/TarHeaders", + x: makeTarHeaders('0'), + y: makeTarHeaders('\x00'), + wantEqual: false, + reason: "type flag differs between the headers", + }, { + label: label + "/NonDeterministicComparer", + x: make([]int, 1000), + y: make([]int, 1000), + opts: []cmp.Option{ + cmp.Comparer(func(_, _ int) bool { + return rand.Intn(2) == 0 + }), + }, + wantPanic: "non-deterministic or non-symmetric function detected", + reason: "non-deterministic comparer", + }, { + label: label + "/NonDeterministicFilter", + x: make([]int, 1000), + y: make([]int, 1000), + opts: []cmp.Option{ + cmp.FilterValues(func(_, _ int) bool { + return rand.Intn(2) == 0 + }, cmp.Ignore()), + }, + wantPanic: "non-deterministic or non-symmetric function detected", + reason: "non-deterministic filter", + }, { + label: label + "/AssymetricComparer", + x: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + y: []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y int) bool { + return x < y + }), + }, + wantPanic: "non-deterministic or non-symmetric function detected", + reason: "asymmetric comparer", + }, { + label: label + "/NonDeterministicTransformer", + x: make([]string, 1000), + y: make([]string, 1000), + opts: []cmp.Option{ + cmp.Transformer("λ", func(x string) int { + return rand.Int() + }), + }, + wantPanic: "non-deterministic function detected", + reason: "non-deterministic transformer", + }, { + label: label + "/IrreflexiveComparison", + x: make([]int, 10), + y: make([]int, 10), + opts: []cmp.Option{ + cmp.Transformer("λ", func(x int) float64 { + return math.NaN() + }), + }, + wantEqual: false, + reason: "dynamic checks should not panic for non-reflexive comparisons", + }, { + label: label + "/StringerMapKey", + x: map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}}, + y: map[*pb.Stringer]*pb.Stringer(nil), + wantEqual: false, + reason: "stringer should be used to format the map key", + }, { + label: label + "/StringerBacktick", + x: []*pb.Stringer{{`multi\nline\nline\nline`}}, + wantEqual: false, + reason: "stringer should use backtick quoting if more readable", + }, { + label: label + "/AvoidPanicAssignableConverter", + x: struct{ I Iface2 }{}, + y: struct{ I Iface2 }{}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y Iface1) bool { + return x == nil && y == nil + }), + }, + wantEqual: true, + reason: "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143", + }, { + label: label + "/AvoidPanicAssignableTransformer", + x: struct{ I Iface2 }{}, + y: struct{ I Iface2 }{}, + opts: []cmp.Option{ + cmp.Transformer("λ", func(v Iface1) bool { + return v == nil + }), + }, + wantEqual: true, + reason: "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143", + }, { + label: label + "/AvoidPanicAssignableFilter", + x: struct{ I Iface2 }{}, + y: struct{ I Iface2 }{}, + opts: []cmp.Option{ + cmp.FilterValues(func(x, y Iface1) bool { + return x == nil && y == nil + }, cmp.Ignore()), + }, + wantEqual: true, + reason: "function call using Go reflection should automatically convert assignable interfaces; see https://golang.org/issues/22143", + }, { + label: label + "/DynamicMap", + x: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}}, + y: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65.0, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63.0, "name": "Sammy Sosa"}}, + wantEqual: false, + reason: "dynamic map with differing types (but semantically equivalent values) should be inequal", + }, { + label: label + "/MapKeyPointer", + x: map[*int]string{ + new(int): "hello", + }, + y: map[*int]string{ + new(int): "world", + }, + wantEqual: false, + reason: "map keys should use shallow (rather than deep) pointer comparison", + }, { + label: label + "/IgnoreSliceElements", + x: [2][]int{ + {0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 0, 9, 0, 0}, + {0, 1, 0, 0, 0, 20}, + }, + y: [2][]int{ + {1, 2, 3, 0, 4, 5, 6, 7, 0, 8, 9, 0, 0, 0}, + {0, 0, 1, 2, 0, 0, 0}, + }, + opts: []cmp.Option{ + cmp.FilterPath(func(p cmp.Path) bool { + vx, vy := p.Last().Values() + if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 { + return true + } + if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 { + return true + } + return false + }, cmp.Ignore()), + }, + wantEqual: false, + reason: "all zero slice elements are ignored (even if missing)", + }, { + label: label + "/IgnoreMapEntries", + x: [2]map[string]int{ + {"ignore1": 0, "ignore2": 0, "keep1": 1, "keep2": 2, "KEEP3": 3, "IGNORE3": 0}, + {"keep1": 1, "ignore1": 0}, + }, + y: [2]map[string]int{ + {"ignore1": 0, "ignore3": 0, "ignore4": 0, "keep1": 1, "keep2": 2, "KEEP3": 3}, + {"keep1": 1, "keep2": 2, "ignore2": 0}, + }, + opts: []cmp.Option{ + cmp.FilterPath(func(p cmp.Path) bool { + vx, vy := p.Last().Values() + if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 { + return true + } + if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 { + return true + } + return false + }, cmp.Ignore()), + }, + wantEqual: false, + reason: "all zero map entries are ignored (even if missing)", + }, { + label: label + "/PanicUnexportedNamed", + x: namedWithUnexported{}, + y: namedWithUnexported{}, + wantPanic: strconv.Quote(reflect.TypeOf(namedWithUnexported{}).PkgPath()) + ".namedWithUnexported", + reason: "panic on named struct type with unexported field", + }, { + label: label + "/PanicUnexportedUnnamed", + x: struct{ a int }{}, + y: struct{ a int }{}, + wantPanic: strconv.Quote(reflect.TypeOf(namedWithUnexported{}).PkgPath()) + ".(struct { a int })", + reason: "panic on unnamed struct type with unexported field", + }, { + label: label + "/UnaddressableStruct", + x: struct{ s fmt.Stringer }{new(bytes.Buffer)}, + y: struct{ s fmt.Stringer }{nil}, + opts: []cmp.Option{ + cmp.AllowUnexported(struct{ s fmt.Stringer }{}), + cmp.FilterPath(func(p cmp.Path) bool { + if _, ok := p.Last().(cmp.StructField); !ok { + return false + } + + t := p.Index(-1).Type() + vx, vy := p.Index(-1).Values() + pvx, pvy := p.Index(-2).Values() + switch { + case vx.Type() != t: + panic(fmt.Sprintf("inconsistent type: %v != %v", vx.Type(), t)) + case vy.Type() != t: + panic(fmt.Sprintf("inconsistent type: %v != %v", vy.Type(), t)) + case vx.CanAddr() != pvx.CanAddr(): + panic(fmt.Sprintf("inconsistent addressability: %v != %v", vx.CanAddr(), pvx.CanAddr())) + case vy.CanAddr() != pvy.CanAddr(): + panic(fmt.Sprintf("inconsistent addressability: %v != %v", vy.CanAddr(), pvy.CanAddr())) + } + return true + }, cmp.Ignore()), + }, + wantEqual: true, + reason: "verify that exporter does not leak implementation details", + }, { + label: label + "/ErrorPanic", + x: io.EOF, + y: io.EOF, + wantPanic: "consider using cmpopts.EquateErrors", + reason: "suggest cmpopts.EquateErrors when accessing unexported fields of error types", + }, { + label: label + "/ErrorEqual", + x: io.EOF, + y: io.EOF, + opts: []cmp.Option{cmpopts.EquateErrors()}, + wantEqual: true, + reason: "cmpopts.EquateErrors should equate these two errors as sentinel values", + }} +} + +func transformerTests() []test { + type StringBytes struct { + String string + Bytes []byte + } + + const label = "Transformer" + + transformOnce := func(name string, f interface{}) cmp.Option { + xform := cmp.Transformer(name, f) + return cmp.FilterPath(func(p cmp.Path) bool { + for _, ps := range p { + if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform { + return false + } + } + return true + }, xform) + } + + return []test{{ + label: label + "/Uints", + x: uint8(0), + y: uint8(1), + opts: []cmp.Option{ + cmp.Transformer("λ", func(in uint8) uint16 { return uint16(in) }), + cmp.Transformer("λ", func(in uint16) uint32 { return uint32(in) }), + cmp.Transformer("λ", func(in uint32) uint64 { return uint64(in) }), + }, + wantEqual: false, + reason: "transform uint8 -> uint16 -> uint32 -> uint64", + }, { + label: label + "/Ambiguous", + x: 0, + y: 1, + opts: []cmp.Option{ + cmp.Transformer("λ", func(in int) int { return in / 2 }), + cmp.Transformer("λ", func(in int) int { return in }), + }, + wantPanic: "ambiguous set of applicable options", + reason: "both transformers apply on int", + }, { + label: label + "/Filtered", + x: []int{0, -5, 0, -1}, + y: []int{1, 3, 0, -5}, + opts: []cmp.Option{ + cmp.FilterValues( + func(x, y int) bool { return x+y >= 0 }, + cmp.Transformer("λ", func(in int) int64 { return int64(in / 2) }), + ), + cmp.FilterValues( + func(x, y int) bool { return x+y < 0 }, + cmp.Transformer("λ", func(in int) int64 { return int64(in) }), + ), + }, + wantEqual: false, + reason: "disjoint transformers filtered based on the values", + }, { + label: label + "/DisjointOutput", + x: 0, + y: 1, + opts: []cmp.Option{ + cmp.Transformer("λ", func(in int) interface{} { + if in == 0 { + return "zero" + } + return float64(in) + }), + }, + wantEqual: false, + reason: "output type differs based on input value", + }, { + label: label + "/JSON", + x: `{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "isAlive": true, + "address": { + "city": "Los Angeles", + "postalCode": "10021-3100", + "state": "CA", + "streetAddress": "21 2nd Street" + }, + "phoneNumbers": [{ + "type": "home", + "number": "212 555-4321" + },{ + "type": "office", + "number": "646 555-4567" + },{ + "number": "123 456-7890", + "type": "mobile" + }], + "children": [] + }`, + y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25, + "address":{"streetAddress":"21 2nd Street","city":"New York", + "state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home", + "number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{ + "type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`, + opts: []cmp.Option{ + transformOnce("ParseJSON", func(s string) (m map[string]interface{}) { + if err := json.Unmarshal([]byte(s), &m); err != nil { + panic(err) + } + return m + }), + }, + wantEqual: false, + reason: "transformer used to parse JSON input", + }, { + label: label + "/AcyclicString", + x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")}, + y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")}, + opts: []cmp.Option{ + transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }), + transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }), + }, + wantEqual: false, + reason: "string -> []string and []byte -> [][]byte transformer only applied once", + }, { + label: label + "/CyclicString", + x: "a\nb\nc\n", + y: "a\nb\nc\n", + opts: []cmp.Option{ + cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }), + }, + wantPanic: "recursive set of Transformers detected", + reason: "cyclic transformation from string -> []string -> string", + }, { + label: label + "/CyclicComplex", + x: complex64(0), + y: complex64(0), + opts: []cmp.Option{ + cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }), + cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }), + cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }), + }, + wantPanic: "recursive set of Transformers detected", + reason: "cyclic transformation from complex64 -> complex128 -> [2]float64 -> complex64", + }} +} + +func reporterTests() []test { + const label = "Reporter" + + type ( + MyString string + MyByte byte + MyBytes []byte + MyInt int8 + MyInts []int8 + MyUint int16 + MyUints []int16 + MyFloat float32 + MyFloats []float32 + MyComposite struct { + StringA string + StringB MyString + BytesA []byte + BytesB []MyByte + BytesC MyBytes + IntsA []int8 + IntsB []MyInt + IntsC MyInts + UintsA []uint16 + UintsB []MyUint + UintsC MyUints + FloatsA []float32 + FloatsB []MyFloat + FloatsC MyFloats + } + ) + + return []test{{ + label: label + "/PanicStringer", + x: struct{ X fmt.Stringer }{struct{ fmt.Stringer }{nil}}, + y: struct{ X fmt.Stringer }{bytes.NewBuffer(nil)}, + wantEqual: false, + reason: "panic from fmt.Stringer should not crash the reporter", + }, { + label: label + "/PanicError", + x: struct{ X error }{struct{ error }{nil}}, + y: struct{ X error }{errors.New("")}, + wantEqual: false, + reason: "panic from error should not crash the reporter", + }, { + label: label + "/AmbiguousType", + x: foo1.Bar{}, + y: foo2.Bar{}, + wantEqual: false, + reason: "reporter should display the qualified type name to disambiguate between the two values", + }, { + label: label + "/AmbiguousPointer", + x: newInt(0), + y: newInt(0), + opts: []cmp.Option{ + cmp.Comparer(func(x, y *int) bool { return x == y }), + }, + wantEqual: false, + reason: "reporter should display the address to disambiguate between the two values", + }, { + label: label + "/AmbiguousPointerStruct", + x: struct{ I *int }{newInt(0)}, + y: struct{ I *int }{newInt(0)}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y *int) bool { return x == y }), + }, + wantEqual: false, + reason: "reporter should display the address to disambiguate between the two struct fields", + }, { + label: label + "/AmbiguousPointerSlice", + x: []*int{newInt(0)}, + y: []*int{newInt(0)}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y *int) bool { return x == y }), + }, + wantEqual: false, + reason: "reporter should display the address to disambiguate between the two slice elements", + }, { + label: label + "/AmbiguousPointerMap", + x: map[string]*int{"zero": newInt(0)}, + y: map[string]*int{"zero": newInt(0)}, + opts: []cmp.Option{ + cmp.Comparer(func(x, y *int) bool { return x == y }), + }, + wantEqual: false, + reason: "reporter should display the address to disambiguate between the two map values", + }, { + label: label + "/AmbiguousStringer", + x: Stringer("hello"), + y: newStringer("hello"), + wantEqual: false, + reason: "reporter should avoid calling String to disambiguate between the two values", + }, { + label: label + "/AmbiguousStringerStruct", + x: struct{ S fmt.Stringer }{Stringer("hello")}, + y: struct{ S fmt.Stringer }{newStringer("hello")}, + wantEqual: false, + reason: "reporter should avoid calling String to disambiguate between the two struct fields", + }, { + label: label + "/AmbiguousStringerSlice", + x: []fmt.Stringer{Stringer("hello")}, + y: []fmt.Stringer{newStringer("hello")}, + wantEqual: false, + reason: "reporter should avoid calling String to disambiguate between the two slice elements", + }, { + label: label + "/AmbiguousStringerMap", + x: map[string]fmt.Stringer{"zero": Stringer("hello")}, + y: map[string]fmt.Stringer{"zero": newStringer("hello")}, + wantEqual: false, + reason: "reporter should avoid calling String to disambiguate between the two map values", + }, { + label: label + "/AmbiguousSliceHeader", + x: make([]int, 0, 5), + y: make([]int, 0, 1000), + opts: []cmp.Option{ + cmp.Comparer(func(x, y []int) bool { return cap(x) == cap(y) }), + }, + wantEqual: false, + reason: "reporter should display the slice header to disambiguate between the two slice values", + }, { + label: label + "/AmbiguousStringerMapKey", + x: map[interface{}]string{ + nil: "nil", + Stringer("hello"): "goodbye", + foo1.Bar{"fizz"}: "buzz", + }, + y: map[interface{}]string{ + newStringer("hello"): "goodbye", + foo2.Bar{"fizz"}: "buzz", + }, + wantEqual: false, + reason: "reporter should avoid calling String to disambiguate between the two map keys", + }, { + label: label + "/NonAmbiguousStringerMapKey", + x: map[interface{}]string{Stringer("hello"): "goodbye"}, + y: map[interface{}]string{newStringer("fizz"): "buzz"}, + wantEqual: false, + reason: "reporter should call String as there is no ambiguity between the two map keys", + }, { + label: label + "/InvalidUTF8", + x: MyString("\xed\xa0\x80"), + wantEqual: false, + reason: "invalid UTF-8 should format as quoted string", + }, { + label: label + "/UnbatchedSlice", + x: MyComposite{IntsA: []int8{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, + y: MyComposite{IntsA: []int8{10, 11, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, + wantEqual: false, + reason: "unbatched diffing desired since few elements differ", + }, { + label: label + "/BatchedSlice", + x: MyComposite{IntsA: []int8{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, + y: MyComposite{IntsA: []int8{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}}, + wantEqual: false, + reason: "batched diffing desired since many elements differ", + }, { + label: label + "/BatchedWithComparer", + x: MyComposite{BytesA: []byte{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, + y: MyComposite{BytesA: []byte{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}}, + wantEqual: false, + opts: []cmp.Option{ + cmp.Comparer(bytes.Equal), + }, + reason: "batched diffing desired since many elements differ", + }, { + label: label + "/BatchedLong", + x: MyComposite{IntsA: []int8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}}, + wantEqual: false, + reason: "batched output desired for a single slice of primitives unique to one of the inputs", + }, { + label: label + "/BatchedNamedAndUnnamed", + x: MyComposite{ + BytesA: []byte{1, 2, 3}, + BytesB: []MyByte{4, 5, 6}, + BytesC: MyBytes{7, 8, 9}, + IntsA: []int8{-1, -2, -3}, + IntsB: []MyInt{-4, -5, -6}, + IntsC: MyInts{-7, -8, -9}, + UintsA: []uint16{1000, 2000, 3000}, + UintsB: []MyUint{4000, 5000, 6000}, + UintsC: MyUints{7000, 8000, 9000}, + FloatsA: []float32{1.5, 2.5, 3.5}, + FloatsB: []MyFloat{4.5, 5.5, 6.5}, + FloatsC: MyFloats{7.5, 8.5, 9.5}, + }, + y: MyComposite{ + BytesA: []byte{3, 2, 1}, + BytesB: []MyByte{6, 5, 4}, + BytesC: MyBytes{9, 8, 7}, + IntsA: []int8{-3, -2, -1}, + IntsB: []MyInt{-6, -5, -4}, + IntsC: MyInts{-9, -8, -7}, + UintsA: []uint16{3000, 2000, 1000}, + UintsB: []MyUint{6000, 5000, 4000}, + UintsC: MyUints{9000, 8000, 7000}, + FloatsA: []float32{3.5, 2.5, 1.5}, + FloatsB: []MyFloat{6.5, 5.5, 4.5}, + FloatsC: MyFloats{9.5, 8.5, 7.5}, + }, + wantEqual: false, + reason: "batched diffing available for both named and unnamed slices", + }, { + label: label + "/BinaryHexdump", + x: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeX\x95A\xfd$fX\x8byT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1U~{\xf6\xb3~\x1dWi \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")}, + y: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1u-[]]\xf6\xb3haha~\x1dWI \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")}, + wantEqual: false, + reason: "binary diff in hexdump form since data is binary data", + }, { + label: label + "/StringHexdump", + x: MyComposite{StringB: MyString("readme.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000046\x0000000000000\x00011173\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")}, + y: MyComposite{StringB: MyString("gopher.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000043\x0000000000000\x00011217\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")}, + wantEqual: false, + reason: "binary diff desired since string looks like binary data", + }, { + label: label + "/BinaryString", + x: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"314 54th Avenue","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)}, + y: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)}, + wantEqual: false, + reason: "batched textual diff desired since bytes looks like textual data", + }, { + label: label + "/TripleQuote", + x: MyComposite{StringA: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n"}, + y: MyComposite{StringA: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nSSS\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n"}, + wantEqual: false, + reason: "use triple-quote syntax", + }, { + label: label + "/TripleQuoteSlice", + x: []string{ + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + }, + y: []string{ + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\n", + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + }, + wantEqual: false, + reason: "use triple-quote syntax for slices of strings", + }, { + label: label + "/TripleQuoteNamedTypes", + x: MyComposite{ + StringB: MyString("aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"), + BytesC: MyBytes("aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"), + }, + y: MyComposite{ + StringB: MyString("aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nSSS\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"), + BytesC: MyBytes("aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nSSS\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz"), + }, + wantEqual: false, + reason: "use triple-quote syntax for named types", + }, { + label: label + "/TripleQuoteSliceNamedTypes", + x: []MyString{ + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + }, + y: []MyString{ + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\n", + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + }, + wantEqual: false, + reason: "use triple-quote syntax for slices of named strings", + }, { + label: label + "/TripleQuoteEndlines", + x: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\r\nhhh\n\riii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n\r", + y: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\r\nhhh\n\riii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz", + wantEqual: false, + reason: "use triple-quote syntax", + }, { + label: label + "/AvoidTripleQuoteAmbiguousQuotes", + x: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + y: "aaa\nbbb\nCCC\nddd\neee\n\"\"\"\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + wantEqual: false, + reason: "avoid triple-quote syntax due to presence of ambiguous triple quotes", + }, { + label: label + "/AvoidTripleQuoteAmbiguousEllipsis", + x: "aaa\nbbb\nccc\n...\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + y: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + wantEqual: false, + reason: "avoid triple-quote syntax due to presence of ambiguous ellipsis", + }, { + label: label + "/AvoidTripleQuoteNonPrintable", + x: "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + y: "aaa\nbbb\nCCC\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\no\roo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + wantEqual: false, + reason: "use triple-quote syntax", + }, { + label: label + "/AvoidTripleQuoteIdenticalWhitespace", + x: "aaa\nbbb\nccc\n ddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nRRR\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + y: "aaa\nbbb\nccc \nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\nqqq\nrrr\nsss\nttt\nuuu\nvvv\nwww\nxxx\nyyy\nzzz\n", + wantEqual: false, + reason: "avoid triple-quote syntax due to visual equivalence of differences", + }, { + label: label + "/TripleQuoteStringer", + x: []fmt.Stringer{ + bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n}\n")), + bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n\t\"math/rand\"\n)\n\nfunc main() {\n\tfmt.Println(\"My favorite number is\", rand.Intn(10))\n}\n")), + }, + y: []fmt.Stringer{ + bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hello, playground\")\n}\n")), + bytes.NewBuffer([]byte("package main\n\nimport (\n\t\"fmt\"\n\t\"math\"\n)\n\nfunc main() {\n\tfmt.Printf(\"Now you have %g problems.\\n\", math.Sqrt(7))\n}\n")), + }, + opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, + wantEqual: false, + reason: "multi-line String output should be formatted with triple quote", + }, { + label: label + "/LimitMaximumBytesDiffs", + x: []byte("\xcd====\x06\x1f\xc2\xcc\xc2-S=====\x1d\xdfa\xae\x98\x9fH======ǰ\xb7=======\xef====:\\\x94\xe6J\xc7=====\xb4======\n\n\xf7\x94===========\xf2\x9c\xc0f=====4\xf6\xf1\xc3\x17\x82======n\x16`\x91D\xc6\x06=======\x1cE====.===========\xc4\x18=======\x8a\x8d\x0e====\x87\xb1\xa5\x8e\xc3=====z\x0f1\xaeU======G,=======5\xe75\xee\x82\xf4\xce====\x11r===========\xaf]=======z\x05\xb3\x91\x88%\xd2====\n1\x89=====i\xb7\x055\xe6\x81\xd2=============\x883=@̾====\x14\x05\x96%^t\x04=====\xe7Ȉ\x90\x1d============="), + y: []byte("\\====|\x96\xe7SB\xa0\xab=====\xf0\xbd\xa5q\xab\x17;======\xabP\x00=======\xeb====\xa5\x14\xe6O(\xe4=====(======/c@?===========\xd9x\xed\x13=====J\xfc\x918B\x8d======a8A\xebs\x04\xae=======\aC====\x1c===========\x91\"=======uؾ====s\xec\x845\a=====;\xabS9t======\x1f\x1b=======\x80\xab/\xed+:;====\xeaI===========\xabl=======\xb9\xe9\xfdH\x93\x8e\u007f====ח\xe5=====Ig\x88m\xf5\x01V=============\xf7+4\xb0\x92E====\x9fj\xf8&\xd0h\xf9=====\xeeΨ\r\xbf============="), + wantEqual: false, + reason: "total bytes difference output is truncated due to excessive number of differences", + }, { + label: label + "/LimitMaximumStringDiffs", + x: "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\n", + y: "aa\nb\ncc\nd\nee\nf\ngg\nh\nii\nj\nkk\nl\nmm\nn\noo\np\nqq\nr\nss\nt\nuu\nv\nww\nx\nyy\nz\nAA\nB\nCC\nD\nEE\nF\nGG\nH\nII\nJ\nKK\nL\nMM\nN\nOO\nP\nQQ\nR\nSS\nT\nUU\nV\nWW\nX\nYY\nZ\n", + wantEqual: false, + reason: "total string difference output is truncated due to excessive number of differences", + }, { + label: label + "/LimitMaximumSliceDiffs", + x: func() (out []struct{ S string }) { + for _, s := range strings.Split("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\nA\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ\n", "\n") { + out = append(out, struct{ S string }{s}) + } + return out + }(), + y: func() (out []struct{ S string }) { + for _, s := range strings.Split("aa\nb\ncc\nd\nee\nf\ngg\nh\nii\nj\nkk\nl\nmm\nn\noo\np\nqq\nr\nss\nt\nuu\nv\nww\nx\nyy\nz\nAA\nB\nCC\nD\nEE\nF\nGG\nH\nII\nJ\nKK\nL\nMM\nN\nOO\nP\nQQ\nR\nSS\nT\nUU\nV\nWW\nX\nYY\nZ\n", "\n") { + out = append(out, struct{ S string }{s}) + } + return out + }(), + wantEqual: false, + reason: "total slice difference output is truncated due to excessive number of differences", + }, { + label: label + "/MultilineString", + x: MyComposite{ + StringA: strings.TrimPrefix(` +Package cmp determines equality of values. + +This package is intended to be a more powerful and safer alternative to +reflect.DeepEqual for comparing whether two values are semantically equal. + +The primary features of cmp are: + +• When the default behavior of equality does not suit the needs of the test, +custom equality functions can override the equality operation. +For example, an equality function may report floats as equal so long as they +are within some tolerance of each other. + +• Types that have an Equal method may use that method to determine equality. +This allows package authors to determine the equality operation for the types +that they define. + +• If no custom equality functions are used and no Equal method is defined, +equality is determined by recursively comparing the primitive kinds on both +values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported +fields are not compared by default; they result in panics unless suppressed +by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared +using the AllowUnexported option. +`, "\n"), + }, + y: MyComposite{ + StringA: strings.TrimPrefix(` +Package cmp determines equality of value. + +This package is intended to be a more powerful and safer alternative to +reflect.DeepEqual for comparing whether two values are semantically equal. + +The primary features of cmp are: + +• When the default behavior of equality does not suit the needs of the test, +custom equality functions can override the equality operation. +For example, an equality function may report floats as equal so long as they +are within some tolerance of each other. + +• If no custom equality functions are used and no Equal method is defined, +equality is determined by recursively comparing the primitive kinds on both +values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported +fields are not compared by default; they result in panics unless suppressed +by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared +using the AllowUnexported option.`, "\n"), + }, + wantEqual: false, + reason: "batched per-line diff desired since string looks like multi-line textual data", + }, { + label: label + "/Slices", + x: MyComposite{ + BytesA: []byte{1, 2, 3}, + BytesB: []MyByte{4, 5, 6}, + BytesC: MyBytes{7, 8, 9}, + IntsA: []int8{-1, -2, -3}, + IntsB: []MyInt{-4, -5, -6}, + IntsC: MyInts{-7, -8, -9}, + UintsA: []uint16{1000, 2000, 3000}, + UintsB: []MyUint{4000, 5000, 6000}, + UintsC: MyUints{7000, 8000, 9000}, + FloatsA: []float32{1.5, 2.5, 3.5}, + FloatsB: []MyFloat{4.5, 5.5, 6.5}, + FloatsC: MyFloats{7.5, 8.5, 9.5}, + }, + y: MyComposite{}, + wantEqual: false, + reason: "batched diffing for non-nil slices and nil slices", + }, { + label: label + "/EmptySlices", + x: MyComposite{ + BytesA: []byte{}, + BytesB: []MyByte{}, + BytesC: MyBytes{}, + IntsA: []int8{}, + IntsB: []MyInt{}, + IntsC: MyInts{}, + UintsA: []uint16{}, + UintsB: []MyUint{}, + UintsC: MyUints{}, + FloatsA: []float32{}, + FloatsB: []MyFloat{}, + FloatsC: MyFloats{}, + }, + y: MyComposite{}, + wantEqual: false, + reason: "batched diffing for empty slices and nil slices", + }, { + label: label + "/LargeMapKey", + x: map[*[]byte]int{func() *[]byte { + b := make([]byte, 1<<20, 1<<20) + return &b + }(): 0}, + y: map[*[]byte]int{func() *[]byte { + b := make([]byte, 1<<20, 1<<20) + return &b + }(): 0}, + reason: "printing map keys should have some verbosity limit imposed", + }, { + label: label + "/LargeStringInInterface", + x: struct{ X interface{} }{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis."}, + y: struct{ X interface{} }{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis,"}, + reason: "strings within an interface should benefit from specialized diffing", + }, { + label: label + "/LargeBytesInInterface", + x: struct{ X interface{} }{[]byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis.")}, + y: struct{ X interface{} }{[]byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis,")}, + reason: "bytes slice within an interface should benefit from specialized diffing", + }, { + label: label + "/LargeStandaloneString", + x: struct{ X interface{} }{[1]string{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis."}}, + y: struct{ X interface{} }{[1]string{"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis,"}}, + reason: "printing a large standalone string that is different should print enough context to see the difference", + }} +} + +func embeddedTests() []test { + const label = "EmbeddedStruct" + + privateStruct := *new(ts.ParentStructA).PrivateStruct() + + createStructA := func(i int) ts.ParentStructA { + s := ts.ParentStructA{} + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + return s + } + + createStructB := func(i int) ts.ParentStructB { + s := ts.ParentStructB{} + s.PublicStruct.Public = 1 + i + s.PublicStruct.SetPrivate(2 + i) + return s + } + + createStructC := func(i int) ts.ParentStructC { + s := ts.ParentStructC{} + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + s.Public = 3 + i + s.SetPrivate(4 + i) + return s + } + + createStructD := func(i int) ts.ParentStructD { + s := ts.ParentStructD{} + s.PublicStruct.Public = 1 + i + s.PublicStruct.SetPrivate(2 + i) + s.Public = 3 + i + s.SetPrivate(4 + i) + return s + } + + createStructE := func(i int) ts.ParentStructE { + s := ts.ParentStructE{} + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + s.PublicStruct.Public = 3 + i + s.PublicStruct.SetPrivate(4 + i) + return s + } + + createStructF := func(i int) ts.ParentStructF { + s := ts.ParentStructF{} + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + s.PublicStruct.Public = 3 + i + s.PublicStruct.SetPrivate(4 + i) + s.Public = 5 + i + s.SetPrivate(6 + i) + return s + } + + createStructG := func(i int) *ts.ParentStructG { + s := ts.NewParentStructG() + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + return s + } + + createStructH := func(i int) *ts.ParentStructH { + s := ts.NewParentStructH() + s.PublicStruct.Public = 1 + i + s.PublicStruct.SetPrivate(2 + i) + return s + } + + createStructI := func(i int) *ts.ParentStructI { + s := ts.NewParentStructI() + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + s.PublicStruct.Public = 3 + i + s.PublicStruct.SetPrivate(4 + i) + return s + } + + createStructJ := func(i int) *ts.ParentStructJ { + s := ts.NewParentStructJ() + s.PrivateStruct().Public = 1 + i + s.PrivateStruct().SetPrivate(2 + i) + s.PublicStruct.Public = 3 + i + s.PublicStruct.SetPrivate(4 + i) + s.Private().Public = 5 + i + s.Private().SetPrivate(6 + i) + s.Public.Public = 7 + i + s.Public.SetPrivate(8 + i) + return s + } + + // TODO(≥go1.10): Workaround for reflect bug (https://golang.org/issue/21122). + wantPanicNotGo110 := func(s string) string { + if !flags.AtLeastGo110 { + return "" + } + return s + } + + return []test{{ + label: label + "/ParentStructA/PanicUnexported1", + x: ts.ParentStructA{}, + y: ts.ParentStructA{}, + wantPanic: "cannot handle unexported field", + reason: "ParentStructA has an unexported field", + }, { + label: label + "/ParentStructA/Ignored", + x: ts.ParentStructA{}, + y: ts.ParentStructA{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructA{}), + }, + wantEqual: true, + reason: "the only field (which is unexported) of ParentStructA is ignored", + }, { + label: label + "/ParentStructA/PanicUnexported2", + x: createStructA(0), + y: createStructA(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructA{}), + }, + wantPanic: "cannot handle unexported field", + reason: "privateStruct also has unexported fields", + }, { + label: label + "/ParentStructA/Equal", + x: createStructA(0), + y: createStructA(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructA{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructA and privateStruct are allowed", + }, { + label: label + "/ParentStructA/Inequal", + x: createStructA(0), + y: createStructA(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructA{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructB/PanicUnexported1", + x: ts.ParentStructB{}, + y: ts.ParentStructB{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructB{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct has an unexported field", + }, { + label: label + "/ParentStructB/Ignored", + x: ts.ParentStructB{}, + y: ts.ParentStructB{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructB{}), + cmpopts.IgnoreUnexported(ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructB and PublicStruct are ignored", + }, { + label: label + "/ParentStructB/PanicUnexported2", + x: createStructB(0), + y: createStructB(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructB{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct also has unexported fields", + }, { + label: label + "/ParentStructB/Equal", + x: createStructB(0), + y: createStructB(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructB and PublicStruct are allowed", + }, { + label: label + "/ParentStructB/Inequal", + x: createStructB(0), + y: createStructB(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructC/PanicUnexported1", + x: ts.ParentStructC{}, + y: ts.ParentStructC{}, + wantPanic: "cannot handle unexported field", + reason: "ParentStructC has unexported fields", + }, { + label: label + "/ParentStructC/Ignored", + x: ts.ParentStructC{}, + y: ts.ParentStructC{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructC{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructC are ignored", + }, { + label: label + "/ParentStructC/PanicUnexported2", + x: createStructC(0), + y: createStructC(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructC{}), + }, + wantPanic: "cannot handle unexported field", + reason: "privateStruct also has unexported fields", + }, { + label: label + "/ParentStructC/Equal", + x: createStructC(0), + y: createStructC(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructC{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructC and privateStruct are allowed", + }, { + label: label + "/ParentStructC/Inequal", + x: createStructC(0), + y: createStructC(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructC{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructD/PanicUnexported1", + x: ts.ParentStructD{}, + y: ts.ParentStructD{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructD{}), + }, + wantPanic: "cannot handle unexported field", + reason: "ParentStructD has unexported fields", + }, { + label: label + "/ParentStructD/Ignored", + x: ts.ParentStructD{}, + y: ts.ParentStructD{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructD{}), + cmpopts.IgnoreUnexported(ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructD and PublicStruct are ignored", + }, { + label: label + "/ParentStructD/PanicUnexported2", + x: createStructD(0), + y: createStructD(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructD{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct also has unexported fields", + }, { + label: label + "/ParentStructD/Equal", + x: createStructD(0), + y: createStructD(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructD and PublicStruct are allowed", + }, { + label: label + "/ParentStructD/Inequal", + x: createStructD(0), + y: createStructD(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructE/PanicUnexported1", + x: ts.ParentStructE{}, + y: ts.ParentStructE{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructE{}), + }, + wantPanic: "cannot handle unexported field", + reason: "ParentStructE has unexported fields", + }, { + label: label + "/ParentStructE/Ignored", + x: ts.ParentStructE{}, + y: ts.ParentStructE{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructE{}), + cmpopts.IgnoreUnexported(ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructE and PublicStruct are ignored", + }, { + label: label + "/ParentStructE/PanicUnexported2", + x: createStructE(0), + y: createStructE(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructE{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct and privateStruct also has unexported fields", + }, { + label: label + "/ParentStructE/PanicUnexported3", + x: createStructE(0), + y: createStructE(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}), + }, + wantPanic: "cannot handle unexported field", + reason: "privateStruct also has unexported fields", + }, { + label: label + "/ParentStructE/Equal", + x: createStructE(0), + y: createStructE(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructE, PublicStruct, and privateStruct are allowed", + }, { + label: label + "/ParentStructE/Inequal", + x: createStructE(0), + y: createStructE(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructF/PanicUnexported1", + x: ts.ParentStructF{}, + y: ts.ParentStructF{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructF{}), + }, + wantPanic: "cannot handle unexported field", + reason: "ParentStructF has unexported fields", + }, { + label: label + "/ParentStructF/Ignored", + x: ts.ParentStructF{}, + y: ts.ParentStructF{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructF{}), + cmpopts.IgnoreUnexported(ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructF and PublicStruct are ignored", + }, { + label: label + "/ParentStructF/PanicUnexported2", + x: createStructF(0), + y: createStructF(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructF{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct and privateStruct also has unexported fields", + }, { + label: label + "/ParentStructF/PanicUnexported3", + x: createStructF(0), + y: createStructF(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}), + }, + wantPanic: "cannot handle unexported field", + reason: "privateStruct also has unexported fields", + }, { + label: label + "/ParentStructF/Equal", + x: createStructF(0), + y: createStructF(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructF, PublicStruct, and privateStruct are allowed", + }, { + label: label + "/ParentStructF/Inequal", + x: createStructF(0), + y: createStructF(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructG/PanicUnexported1", + x: ts.ParentStructG{}, + y: ts.ParentStructG{}, + wantPanic: wantPanicNotGo110("cannot handle unexported field"), + wantEqual: !flags.AtLeastGo110, + reason: "ParentStructG has unexported fields", + }, { + label: label + "/ParentStructG/Ignored", + x: ts.ParentStructG{}, + y: ts.ParentStructG{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructG{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructG are ignored", + }, { + label: label + "/ParentStructG/PanicUnexported2", + x: createStructG(0), + y: createStructG(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructG{}), + }, + wantPanic: "cannot handle unexported field", + reason: "privateStruct also has unexported fields", + }, { + label: label + "/ParentStructG/Equal", + x: createStructG(0), + y: createStructG(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructG{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructG and privateStruct are allowed", + }, { + label: label + "/ParentStructG/Inequal", + x: createStructG(0), + y: createStructG(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructG{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructH/EqualNil", + x: ts.ParentStructH{}, + y: ts.ParentStructH{}, + wantEqual: true, + reason: "PublicStruct is not compared because the pointer is nil", + }, { + label: label + "/ParentStructH/PanicUnexported1", + x: createStructH(0), + y: createStructH(0), + wantPanic: "cannot handle unexported field", + reason: "PublicStruct has unexported fields", + }, { + label: label + "/ParentStructH/Ignored", + x: ts.ParentStructH{}, + y: ts.ParentStructH{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructH{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructH are ignored (it has none)", + }, { + label: label + "/ParentStructH/PanicUnexported2", + x: createStructH(0), + y: createStructH(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructH{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct also has unexported fields", + }, { + label: label + "/ParentStructH/Equal", + x: createStructH(0), + y: createStructH(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructH and PublicStruct are allowed", + }, { + label: label + "/ParentStructH/Inequal", + x: createStructH(0), + y: createStructH(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructI/PanicUnexported1", + x: ts.ParentStructI{}, + y: ts.ParentStructI{}, + wantPanic: wantPanicNotGo110("cannot handle unexported field"), + wantEqual: !flags.AtLeastGo110, + reason: "ParentStructI has unexported fields", + }, { + label: label + "/ParentStructI/Ignored1", + x: ts.ParentStructI{}, + y: ts.ParentStructI{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructI{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructI are ignored", + }, { + label: label + "/ParentStructI/PanicUnexported2", + x: createStructI(0), + y: createStructI(0), + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructI{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct and privateStruct also has unexported fields", + }, { + label: label + "/ParentStructI/Ignored2", + x: createStructI(0), + y: createStructI(0), + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructI and PublicStruct are ignored", + }, { + label: label + "/ParentStructI/PanicUnexported3", + x: createStructI(0), + y: createStructI(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructI{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct and privateStruct also has unexported fields", + }, { + label: label + "/ParentStructI/Equal", + x: createStructI(0), + y: createStructI(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructI, PublicStruct, and privateStruct are allowed", + }, { + label: label + "/ParentStructI/Inequal", + x: createStructI(0), + y: createStructI(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }, { + label: label + "/ParentStructJ/PanicUnexported1", + x: ts.ParentStructJ{}, + y: ts.ParentStructJ{}, + wantPanic: "cannot handle unexported field", + reason: "ParentStructJ has unexported fields", + }, { + label: label + "/ParentStructJ/PanicUnexported2", + x: ts.ParentStructJ{}, + y: ts.ParentStructJ{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructJ{}), + }, + wantPanic: "cannot handle unexported field", + reason: "PublicStruct and privateStruct also has unexported fields", + }, { + label: label + "/ParentStructJ/Ignored", + x: ts.ParentStructJ{}, + y: ts.ParentStructJ{}, + opts: []cmp.Option{ + cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}), + }, + wantEqual: true, + reason: "unexported fields of ParentStructJ and PublicStruct are ignored", + }, { + label: label + "/ParentStructJ/PanicUnexported3", + x: createStructJ(0), + y: createStructJ(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}), + }, + wantPanic: "cannot handle unexported field", + reason: "privateStruct also has unexported fields", + }, { + label: label + "/ParentStructJ/Equal", + x: createStructJ(0), + y: createStructJ(0), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: true, + reason: "unexported fields of both ParentStructJ, PublicStruct, and privateStruct are allowed", + }, { + label: label + "/ParentStructJ/Inequal", + x: createStructJ(0), + y: createStructJ(1), + opts: []cmp.Option{ + cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct), + }, + wantEqual: false, + reason: "the two values differ on some fields", + }} +} + +func methodTests() []test { + const label = "EqualMethod" + + // A common mistake that the Equal method is on a pointer receiver, + // but only a non-pointer value is present in the struct. + // A transform can be used to forcibly reference the value. + addrTransform := cmp.FilterPath(func(p cmp.Path) bool { + if len(p) == 0 { + return false + } + t := p[len(p)-1].Type() + if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr { + return false + } + if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok { + tf := m.Func.Type() + return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 && + tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true) + } + return false + }, cmp.Transformer("Addr", func(x interface{}) interface{} { + v := reflect.ValueOf(x) + vp := reflect.New(v.Type()) + vp.Elem().Set(v) + return vp.Interface() + })) + + // For each of these types, there is an Equal method defined, which always + // returns true, while the underlying data are fundamentally different. + // Since the method should be called, these are expected to be equal. + return []test{{ + label: label + "/StructA/ValueEqual", + x: ts.StructA{X: "NotEqual"}, + y: ts.StructA{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructA value called", + }, { + label: label + "/StructA/PointerEqual", + x: &ts.StructA{X: "NotEqual"}, + y: &ts.StructA{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructA pointer called", + }, { + label: label + "/StructB/ValueInequal", + x: ts.StructB{X: "NotEqual"}, + y: ts.StructB{X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructB value not called", + }, { + label: label + "/StructB/ValueAddrEqual", + x: ts.StructB{X: "NotEqual"}, + y: ts.StructB{X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructB pointer called due to shallow copy transform", + }, { + label: label + "/StructB/PointerEqual", + x: &ts.StructB{X: "NotEqual"}, + y: &ts.StructB{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructB pointer called", + }, { + label: label + "/StructC/ValueEqual", + x: ts.StructC{X: "NotEqual"}, + y: ts.StructC{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructC value called", + }, { + label: label + "/StructC/PointerEqual", + x: &ts.StructC{X: "NotEqual"}, + y: &ts.StructC{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructC pointer called", + }, { + label: label + "/StructD/ValueInequal", + x: ts.StructD{X: "NotEqual"}, + y: ts.StructD{X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructD value not called", + }, { + label: label + "/StructD/ValueAddrEqual", + x: ts.StructD{X: "NotEqual"}, + y: ts.StructD{X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructD pointer called due to shallow copy transform", + }, { + label: label + "/StructD/PointerEqual", + x: &ts.StructD{X: "NotEqual"}, + y: &ts.StructD{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructD pointer called", + }, { + label: label + "/StructE/ValueInequal", + x: ts.StructE{X: "NotEqual"}, + y: ts.StructE{X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructE value not called", + }, { + label: label + "/StructE/ValueAddrEqual", + x: ts.StructE{X: "NotEqual"}, + y: ts.StructE{X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructE pointer called due to shallow copy transform", + }, { + label: label + "/StructE/PointerEqual", + x: &ts.StructE{X: "NotEqual"}, + y: &ts.StructE{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructE pointer called", + }, { + label: label + "/StructF/ValueInequal", + x: ts.StructF{X: "NotEqual"}, + y: ts.StructF{X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructF value not called", + }, { + label: label + "/StructF/PointerEqual", + x: &ts.StructF{X: "NotEqual"}, + y: &ts.StructF{X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructF pointer called", + }, { + label: label + "/StructA1/ValueEqual", + x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"}, + y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"}, + wantEqual: true, + reason: "Equal method on StructA value called with equal X field", + }, { + label: label + "/StructA1/ValueInequal", + x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructA value called, but inequal X field", + }, { + label: label + "/StructA1/PointerEqual", + x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"}, + y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"}, + wantEqual: true, + reason: "Equal method on StructA value called with equal X field", + }, { + label: label + "/StructA1/PointerInequal", + x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructA value called, but inequal X field", + }, { + label: label + "/StructB1/ValueEqual", + x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"}, + y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructB pointer called due to shallow copy transform with equal X field", + }, { + label: label + "/StructB1/ValueInequal", + x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: false, + reason: "Equal method on StructB pointer called due to shallow copy transform, but inequal X field", + }, { + label: label + "/StructB1/PointerEqual", + x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"}, + y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructB pointer called due to shallow copy transform with equal X field", + }, { + label: label + "/StructB1/PointerInequal", + x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: false, + reason: "Equal method on StructB pointer called due to shallow copy transform, but inequal X field", + }, { + label: label + "/StructC1/ValueEqual", + x: ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructC1 value called", + }, { + label: label + "/StructC1/PointerEqual", + x: &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructC1 pointer called", + }, { + label: label + "/StructD1/ValueInequal", + x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructD1 value not called", + }, { + label: label + "/StructD1/PointerAddrEqual", + x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructD1 pointer called due to shallow copy transform", + }, { + label: label + "/StructD1/PointerEqual", + x: &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructD1 pointer called", + }, { + label: label + "/StructE1/ValueInequal", + x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructE1 value not called", + }, { + label: label + "/StructE1/ValueAddrEqual", + x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, + opts: []cmp.Option{addrTransform}, + wantEqual: true, + reason: "Equal method on StructE1 pointer called due to shallow copy transform", + }, { + label: label + "/StructE1/PointerEqual", + x: &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructE1 pointer called", + }, { + label: label + "/StructF1/ValueInequal", + x: ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructF1 value not called", + }, { + label: label + "/StructF1/PointerEqual", + x: &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method on StructF1 pointer called", + }, { + label: label + "/StructA2/ValueEqual", + x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"}, + y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"}, + wantEqual: true, + reason: "Equal method on StructA pointer called with equal X field", + }, { + label: label + "/StructA2/ValueInequal", + x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructA pointer called, but inequal X field", + }, { + label: label + "/StructA2/PointerEqual", + x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"}, + y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"}, + wantEqual: true, + reason: "Equal method on StructA pointer called with equal X field", + }, { + label: label + "/StructA2/PointerInequal", + x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructA pointer called, but inequal X field", + }, { + label: label + "/StructB2/ValueEqual", + x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"}, + y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"}, + wantEqual: true, + reason: "Equal method on StructB pointer called with equal X field", + }, { + label: label + "/StructB2/ValueInequal", + x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructB pointer called, but inequal X field", + }, { + label: label + "/StructB2/PointerEqual", + x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"}, + y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"}, + wantEqual: true, + reason: "Equal method on StructB pointer called with equal X field", + }, { + label: label + "/StructB2/PointerInequal", + x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"}, + wantEqual: false, + reason: "Equal method on StructB pointer called, but inequal X field", + }, { + label: label + "/StructC2/ValueEqual", + x: ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructC2 value due to forwarded StructC pointer", + }, { + label: label + "/StructC2/PointerEqual", + x: &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructC2 pointer due to forwarded StructC pointer", + }, { + label: label + "/StructD2/ValueEqual", + x: ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructD2 value due to forwarded StructD pointer", + }, { + label: label + "/StructD2/PointerEqual", + x: &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructD2 pointer due to forwarded StructD pointer", + }, { + label: label + "/StructE2/ValueEqual", + x: ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructE2 value due to forwarded StructE pointer", + }, { + label: label + "/StructE2/PointerEqual", + x: &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructE2 pointer due to forwarded StructE pointer", + }, { + label: label + "/StructF2/ValueEqual", + x: ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"}, + y: ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructF2 value due to forwarded StructF pointer", + }, { + label: label + "/StructF2/PointerEqual", + x: &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"}, + y: &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"}, + wantEqual: true, + reason: "Equal method called on StructF2 pointer due to forwarded StructF pointer", + }, { + label: label + "/StructNo/Inequal", + x: ts.StructNo{X: "NotEqual"}, + y: ts.StructNo{X: "not_equal"}, + wantEqual: false, + reason: "Equal method not called since StructNo is not assignable to InterfaceA", + }, { + label: label + "/AssignA/Equal", + x: ts.AssignA(func() int { return 0 }), + y: ts.AssignA(func() int { return 1 }), + wantEqual: true, + reason: "Equal method called since named func is assignable to unnamed func", + }, { + label: label + "/AssignB/Equal", + x: ts.AssignB(struct{ A int }{0}), + y: ts.AssignB(struct{ A int }{1}), + wantEqual: true, + reason: "Equal method called since named struct is assignable to unnamed struct", + }, { + label: label + "/AssignC/Equal", + x: ts.AssignC(make(chan bool)), + y: ts.AssignC(make(chan bool)), + wantEqual: true, + reason: "Equal method called since named channel is assignable to unnamed channel", + }, { + label: label + "/AssignD/Equal", + x: ts.AssignD(make(chan bool)), + y: ts.AssignD(make(chan bool)), + wantEqual: true, + reason: "Equal method called since named channel is assignable to unnamed channel", + }} +} + +type ( + CycleAlpha struct { + Name string + Bravos map[string]*CycleBravo + } + CycleBravo struct { + ID int + Name string + Mods int + Alphas map[string]*CycleAlpha + } +) + +func cycleTests() []test { + const label = "Cycle" + + type ( + P *P + S []S + M map[int]M + ) + + makeGraph := func() map[string]*CycleAlpha { + v := map[string]*CycleAlpha{ + "Foo": &CycleAlpha{ + Name: "Foo", + Bravos: map[string]*CycleBravo{ + "FooBravo": &CycleBravo{ + Name: "FooBravo", + ID: 101, + Mods: 100, + Alphas: map[string]*CycleAlpha{ + "Foo": nil, // cyclic reference + }, + }, + }, + }, + "Bar": &CycleAlpha{ + Name: "Bar", + Bravos: map[string]*CycleBravo{ + "BarBuzzBravo": &CycleBravo{ + Name: "BarBuzzBravo", + ID: 102, + Mods: 2, + Alphas: map[string]*CycleAlpha{ + "Bar": nil, // cyclic reference + "Buzz": nil, // cyclic reference + }, + }, + "BuzzBarBravo": &CycleBravo{ + Name: "BuzzBarBravo", + ID: 103, + Mods: 0, + Alphas: map[string]*CycleAlpha{ + "Bar": nil, // cyclic reference + "Buzz": nil, // cyclic reference + }, + }, + }, + }, + "Buzz": &CycleAlpha{ + Name: "Buzz", + Bravos: map[string]*CycleBravo{ + "BarBuzzBravo": nil, // cyclic reference + "BuzzBarBravo": nil, // cyclic reference + }, + }, + } + v["Foo"].Bravos["FooBravo"].Alphas["Foo"] = v["Foo"] + v["Bar"].Bravos["BarBuzzBravo"].Alphas["Bar"] = v["Bar"] + v["Bar"].Bravos["BarBuzzBravo"].Alphas["Buzz"] = v["Buzz"] + v["Bar"].Bravos["BuzzBarBravo"].Alphas["Bar"] = v["Bar"] + v["Bar"].Bravos["BuzzBarBravo"].Alphas["Buzz"] = v["Buzz"] + v["Buzz"].Bravos["BarBuzzBravo"] = v["Bar"].Bravos["BarBuzzBravo"] + v["Buzz"].Bravos["BuzzBarBravo"] = v["Bar"].Bravos["BuzzBarBravo"] + return v + } + + var tests []test + type XY struct{ x, y interface{} } + for _, tt := range []struct { + label string + in XY + wantEqual bool + reason string + }{{ + label: "PointersEqual", + in: func() XY { + x := new(P) + *x = x + y := new(P) + *y = y + return XY{x, y} + }(), + wantEqual: true, + reason: "equal pair of single-node pointers", + }, { + label: "PointersInequal", + in: func() XY { + x := new(P) + *x = x + y1, y2 := new(P), new(P) + *y1 = y2 + *y2 = y1 + return XY{x, y1} + }(), + wantEqual: false, + reason: "inequal pair of single-node and double-node pointers", + }, { + label: "SlicesEqual", + in: func() XY { + x := S{nil} + x[0] = x + y := S{nil} + y[0] = y + return XY{x, y} + }(), + wantEqual: true, + reason: "equal pair of single-node slices", + }, { + label: "SlicesInequal", + in: func() XY { + x := S{nil} + x[0] = x + y1, y2 := S{nil}, S{nil} + y1[0] = y2 + y2[0] = y1 + return XY{x, y1} + }(), + wantEqual: false, + reason: "inequal pair of single-node and double node slices", + }, { + label: "MapsEqual", + in: func() XY { + x := M{0: nil} + x[0] = x + y := M{0: nil} + y[0] = y + return XY{x, y} + }(), + wantEqual: true, + reason: "equal pair of single-node maps", + }, { + label: "MapsInequal", + in: func() XY { + x := M{0: nil} + x[0] = x + y1, y2 := M{0: nil}, M{0: nil} + y1[0] = y2 + y2[0] = y1 + return XY{x, y1} + }(), + wantEqual: false, + reason: "inequal pair of single-node and double-node maps", + }, { + label: "GraphEqual", + in: XY{makeGraph(), makeGraph()}, + wantEqual: true, + reason: "graphs are equal since they have identical forms", + }, { + label: "GraphInequalZeroed", + in: func() XY { + x := makeGraph() + y := makeGraph() + y["Foo"].Bravos["FooBravo"].ID = 0 + y["Bar"].Bravos["BarBuzzBravo"].ID = 0 + y["Bar"].Bravos["BuzzBarBravo"].ID = 0 + return XY{x, y} + }(), + wantEqual: false, + reason: "graphs are inequal because the ID fields are different", + }, { + label: "GraphInequalStruct", + in: func() XY { + x := makeGraph() + y := makeGraph() + x["Buzz"].Bravos["BuzzBarBravo"] = &CycleBravo{ + Name: "BuzzBarBravo", + ID: 103, + } + return XY{x, y} + }(), + wantEqual: false, + reason: "graphs are inequal because they differ on a map element", + }} { + tests = append(tests, test{ + label: label + "/" + tt.label, + x: tt.in.x, + y: tt.in.y, + wantEqual: tt.wantEqual, + reason: tt.reason, + }) + } + return tests +} + +func project1Tests() []test { + const label = "Project1" + + ignoreUnexported := cmpopts.IgnoreUnexported( + ts.EagleImmutable{}, + ts.DreamerImmutable{}, + ts.SlapImmutable{}, + ts.GoatImmutable{}, + ts.DonkeyImmutable{}, + ts.LoveRadius{}, + ts.SummerLove{}, + ts.SummerLoveSummary{}, + ) + + createEagle := func() ts.Eagle { + return ts.Eagle{ + Name: "eagle", + Hounds: []string{"buford", "tannen"}, + Desc: "some description", + Dreamers: []ts.Dreamer{{}, { + Name: "dreamer2", + Animal: []interface{}{ + ts.Goat{ + Target: "corporation", + Immutable: &ts.GoatImmutable{ + ID: "southbay", + State: (*pb.Goat_States)(newInt(5)), + Started: now, + }, + }, + ts.Donkey{}, + }, + Amoeba: 53, + }}, + Slaps: []ts.Slap{{ + Name: "slapID", + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, + Immutable: &ts.SlapImmutable{ + ID: "immutableSlap", + MildSlap: true, + Started: now, + LoveRadius: &ts.LoveRadius{ + Summer: &ts.SummerLove{ + Summary: &ts.SummerLoveSummary{ + Devices: []string{"foo", "bar", "baz"}, + ChangeType: []pb.SummerType{1, 2, 3}, + }, + }, + }, + }, + }}, + Immutable: &ts.EagleImmutable{ + ID: "eagleID", + Birthday: now, + MissingCall: (*pb.Eagle_MissingCalls)(newInt(55)), + }, + } + } + + return []test{{ + label: label + "/PanicUnexported", + x: ts.Eagle{Slaps: []ts.Slap{{ + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, + }}}, + y: ts.Eagle{Slaps: []ts.Slap{{ + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, + }}}, + wantPanic: "cannot handle unexported field", + reason: "struct contains unexported fields", + }, { + label: label + "/ProtoEqual", + x: ts.Eagle{Slaps: []ts.Slap{{ + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, + }}}, + y: ts.Eagle{Slaps: []ts.Slap{{ + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, + }}}, + opts: []cmp.Option{cmp.Comparer(pb.Equal)}, + wantEqual: true, + reason: "simulated protobuf messages contain the same values", + }, { + label: label + "/ProtoInequal", + x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, { + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, + }}}, + y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, { + Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}}, + }}}, + opts: []cmp.Option{cmp.Comparer(pb.Equal)}, + wantEqual: false, + reason: "simulated protobuf messages contain different values", + }, { + label: label + "/Equal", + x: createEagle(), + y: createEagle(), + opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)}, + wantEqual: true, + reason: "equal because values are the same", + }, { + label: label + "/Inequal", + x: func() ts.Eagle { + eg := createEagle() + eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2" + eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(newInt(6)) + eg.Slaps[0].Immutable.MildSlap = false + return eg + }(), + y: func() ts.Eagle { + eg := createEagle() + devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices + eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1] + return eg + }(), + opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)}, + wantEqual: false, + reason: "inequal because some values are different", + }} +} + +type germSorter []*pb.Germ + +func (gs germSorter) Len() int { return len(gs) } +func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() } +func (gs germSorter) Swap(i, j int) { gs[i], gs[j] = gs[j], gs[i] } + +func project2Tests() []test { + const label = "Project2" + + sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ { + out := append([]*pb.Germ(nil), in...) // Make copy + sort.Sort(germSorter(out)) + return out + }) + + equalDish := cmp.Comparer(func(x, y *ts.Dish) bool { + if x == nil || y == nil { + return x == nil && y == nil + } + px, err1 := x.Proto() + py, err2 := y.Proto() + if err1 != nil || err2 != nil { + return err1 == err2 + } + return pb.Equal(px, py) + }) + + createBatch := func() ts.GermBatch { + return ts.GermBatch{ + DirtyGerms: map[int32][]*pb.Germ{ + 17: { + {Stringer: pb.Stringer{X: "germ1"}}, + }, + 18: { + {Stringer: pb.Stringer{X: "germ2"}}, + {Stringer: pb.Stringer{X: "germ3"}}, + {Stringer: pb.Stringer{X: "germ4"}}, + }, + }, + GermMap: map[int32]*pb.Germ{ + 13: {Stringer: pb.Stringer{X: "germ13"}}, + 21: {Stringer: pb.Stringer{X: "germ21"}}, + }, + DishMap: map[int32]*ts.Dish{ + 0: ts.CreateDish(nil, io.EOF), + 1: ts.CreateDish(nil, io.ErrUnexpectedEOF), + 2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil), + }, + HasPreviousResult: true, + DirtyID: 10, + GermStrain: 421, + InfectedAt: now, + } + } + + return []test{{ + label: label + "/PanicUnexported", + x: createBatch(), + y: createBatch(), + wantPanic: "cannot handle unexported field", + reason: "struct contains unexported fields", + }, { + label: label + "/Equal", + x: createBatch(), + y: createBatch(), + opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, + wantEqual: true, + reason: "equal because identical values are compared", + }, { + label: label + "/InequalOrder", + x: createBatch(), + y: func() ts.GermBatch { + gb := createBatch() + s := gb.DirtyGerms[18] + s[0], s[1], s[2] = s[1], s[2], s[0] + return gb + }(), + opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish}, + wantEqual: false, + reason: "inequal because slice contains elements in differing order", + }, { + label: label + "/EqualOrder", + x: createBatch(), + y: func() ts.GermBatch { + gb := createBatch() + s := gb.DirtyGerms[18] + s[0], s[1], s[2] = s[1], s[2], s[0] + return gb + }(), + opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, + wantEqual: true, + reason: "equal because unordered slice is sorted using transformer", + }, { + label: label + "/Inequal", + x: func() ts.GermBatch { + gb := createBatch() + delete(gb.DirtyGerms, 17) + gb.DishMap[1] = nil + return gb + }(), + y: func() ts.GermBatch { + gb := createBatch() + gb.DirtyGerms[18] = gb.DirtyGerms[18][:2] + gb.GermStrain = 22 + return gb + }(), + opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, + wantEqual: false, + reason: "inequal because some values are different", + }} +} + +func project3Tests() []test { + const label = "Project3" + + allowVisibility := cmp.AllowUnexported(ts.Dirt{}) + + ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{}) + + transformProtos := cmp.Transformer("λ", func(x pb.Dirt) *pb.Dirt { + return &x + }) + + equalTable := cmp.Comparer(func(x, y ts.Table) bool { + tx, ok1 := x.(*ts.MockTable) + ty, ok2 := y.(*ts.MockTable) + if !ok1 || !ok2 { + panic("table type must be MockTable") + } + return cmp.Equal(tx.State(), ty.State()) + }) + + createDirt := func() (d ts.Dirt) { + d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"})) + d.SetTimestamp(12345) + d.Discord = 554 + d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}} + d.SetWizard(map[string]*pb.Wizard{ + "harry": {Stringer: pb.Stringer{X: "potter"}}, + "albus": {Stringer: pb.Stringer{X: "dumbledore"}}, + }) + d.SetLastTime(54321) + return d + } + + return []test{{ + label: label + "/PanicUnexported1", + x: createDirt(), + y: createDirt(), + wantPanic: "cannot handle unexported field", + reason: "struct contains unexported fields", + }, { + label: label + "/PanicUnexported2", + x: createDirt(), + y: createDirt(), + opts: []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, + wantPanic: "cannot handle unexported field", + reason: "struct contains references to simulated protobuf types with unexported fields", + }, { + label: label + "/Equal", + x: createDirt(), + y: createDirt(), + opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, + wantEqual: true, + reason: "transformer used to create reference to protobuf message so it works with pb.Equal", + }, { + label: label + "/Inequal", + x: func() ts.Dirt { + d := createDirt() + d.SetTable(ts.CreateMockTable([]string{"a", "c"})) + d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}} + return d + }(), + y: func() ts.Dirt { + d := createDirt() + d.Discord = 500 + d.SetWizard(map[string]*pb.Wizard{ + "harry": {Stringer: pb.Stringer{X: "otter"}}, + }) + return d + }(), + opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, + wantEqual: false, + reason: "inequal because some values are different", + }} +} + +func project4Tests() []test { + const label = "Project4" + + allowVisibility := cmp.AllowUnexported( + ts.Cartel{}, + ts.Headquarter{}, + ts.Poison{}, + ) + + transformProtos := cmp.Transformer("λ", func(x pb.Restrictions) *pb.Restrictions { + return &x + }) + + createCartel := func() ts.Cartel { + var p ts.Poison + p.SetPoisonType(5) + p.SetExpiration(now) + p.SetManufacturer("acme") + + var hq ts.Headquarter + hq.SetID(5) + hq.SetLocation("moon") + hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"}) + hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}) + hq.SetPublicMessage([]byte{1, 2, 3, 4, 5}) + hq.SetHorseBack("abcdef") + hq.SetStatus(44) + + var c ts.Cartel + c.Headquarter = hq + c.SetSource("mars") + c.SetCreationTime(now) + c.SetBoss("al capone") + c.SetPoisons([]*ts.Poison{&p}) + + return c + } + + return []test{{ + label: label + "/PanicUnexported1", + x: createCartel(), + y: createCartel(), + wantPanic: "cannot handle unexported field", + reason: "struct contains unexported fields", + }, { + label: label + "/PanicUnexported2", + x: createCartel(), + y: createCartel(), + opts: []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)}, + wantPanic: "cannot handle unexported field", + reason: "struct contains references to simulated protobuf types with unexported fields", + }, { + label: label + "/Equal", + x: createCartel(), + y: createCartel(), + opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)}, + wantEqual: true, + reason: "transformer used to create reference to protobuf message so it works with pb.Equal", + }, { + label: label + "/Inequal", + x: func() ts.Cartel { + d := createCartel() + var p1, p2 ts.Poison + p1.SetPoisonType(1) + p1.SetExpiration(now) + p1.SetManufacturer("acme") + p2.SetPoisonType(2) + p2.SetManufacturer("acme2") + d.SetPoisons([]*ts.Poison{&p1, &p2}) + return d + }(), + y: func() ts.Cartel { + d := createCartel() + d.SetSubDivisions([]string{"bravo", "charlie"}) + d.SetPublicMessage([]byte{1, 2, 4, 3, 5}) + return d + }(), + opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)}, + wantEqual: false, + reason: "inequal because some values are different", + }} +} + +// BenchmarkBytes benchmarks the performance of performing Equal or Diff on +// large slices of bytes. +func BenchmarkBytes(b *testing.B) { + // Create a list of PathFilters that never apply, but are evaluated. + const maxFilters = 5 + var filters cmp.Options + errorIface := reflect.TypeOf((*error)(nil)).Elem() + for i := 0; i <= maxFilters; i++ { + filters = append(filters, cmp.FilterPath(func(p cmp.Path) bool { + return p.Last().Type().AssignableTo(errorIface) // Never true + }, cmp.Ignore())) + } + + type benchSize struct { + label string + size int64 + } + for _, ts := range []benchSize{ + {"4KiB", 1 << 12}, + {"64KiB", 1 << 16}, + {"1MiB", 1 << 20}, + {"16MiB", 1 << 24}, + } { + bx := append(append(make([]byte, ts.size/2), 'x'), make([]byte, ts.size/2)...) + by := append(append(make([]byte, ts.size/2), 'y'), make([]byte, ts.size/2)...) + b.Run(ts.label, func(b *testing.B) { + // Iteratively add more filters that never apply, but are evaluated + // to measure the cost of simply evaluating each filter. + for i := 0; i <= maxFilters; i++ { + b.Run(fmt.Sprintf("EqualFilter%d", i), func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(2 * ts.size) + for j := 0; j < b.N; j++ { + cmp.Equal(bx, by, filters[:i]...) + } + }) + } + for i := 0; i <= maxFilters; i++ { + b.Run(fmt.Sprintf("DiffFilter%d", i), func(b *testing.B) { + b.ReportAllocs() + b.SetBytes(2 * ts.size) + for j := 0; j < b.N; j++ { + cmp.Diff(bx, by, filters[:i]...) + } + }) + } + }) + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_reporter_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_reporter_test.go new file mode 100644 index 0000000..bacba28 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_reporter_test.go @@ -0,0 +1,59 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp_test + +import ( + "fmt" + "strings" + + "github.com/google/go-cmp/cmp" +) + +// DiffReporter is a simple custom reporter that only records differences +// detected during comparison. +type DiffReporter struct { + path cmp.Path + diffs []string +} + +func (r *DiffReporter) PushStep(ps cmp.PathStep) { + r.path = append(r.path, ps) +} + +func (r *DiffReporter) Report(rs cmp.Result) { + if !rs.Equal() { + vx, vy := r.path.Last().Values() + r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy)) + } +} + +func (r *DiffReporter) PopStep() { + r.path = r.path[:len(r.path)-1] +} + +func (r *DiffReporter) String() string { + return strings.Join(r.diffs, "\n") +} + +func ExampleReporter() { + x, y := MakeGatewayInfo() + + var r DiffReporter + cmp.Equal(x, y, cmp.Reporter(&r)) + fmt.Print(r.String()) + + // Output: + // {cmp_test.Gateway}.IPAddress: + // -: 192.168.0.1 + // +: 192.168.0.2 + // + // {cmp_test.Gateway}.Clients[4].IPAddress: + // -: 192.168.0.219 + // +: 192.168.0.221 + // + // {cmp_test.Gateway}.Clients[5->?]: + // -: {Hostname:americano IPAddress:192.168.0.188 LastSeen:2009-11-10 23:03:05 +0000 UTC} + // +: +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_test.go new file mode 100644 index 0000000..d165383 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/example_test.go @@ -0,0 +1,376 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp_test + +import ( + "fmt" + "math" + "net" + "reflect" + "sort" + "strings" + "time" + + "github.com/google/go-cmp/cmp" +) + +// TODO: Re-write these examples in terms of how you actually use the +// fundamental options and filters and not in terms of what cool things you can +// do with them since that overlaps with cmp/cmpopts. + +// Use Diff to print out a human-readable report of differences for tests +// comparing nested or structured data. +func ExampleDiff_testing() { + // Let got be the hypothetical value obtained from some logic under test + // and want be the expected golden data. + got, want := MakeGatewayInfo() + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff) + } + + // Output: + // MakeGatewayInfo() mismatch (-want +got): + // cmp_test.Gateway{ + // SSID: "CoffeeShopWiFi", + // - IPAddress: s"192.168.0.2", + // + IPAddress: s"192.168.0.1", + // NetMask: {0xff, 0xff, 0x00, 0x00}, + // Clients: []cmp_test.Client{ + // ... // 2 identical elements + // {Hostname: "macchiato", IPAddress: s"192.168.0.153", LastSeen: s"2009-11-10 23:39:43 +0000 UTC"}, + // {Hostname: "espresso", IPAddress: s"192.168.0.121"}, + // { + // Hostname: "latte", + // - IPAddress: s"192.168.0.221", + // + IPAddress: s"192.168.0.219", + // LastSeen: s"2009-11-10 23:00:23 +0000 UTC", + // }, + // + { + // + Hostname: "americano", + // + IPAddress: s"192.168.0.188", + // + LastSeen: s"2009-11-10 23:03:05 +0000 UTC", + // + }, + // }, + // } +} + +// Approximate equality for floats can be handled by defining a custom +// comparer on floats that determines two values to be equal if they are within +// some range of each other. +// +// This example is for demonstrative purposes; use cmpopts.EquateApprox instead. +func ExampleOption_approximateFloats() { + // This Comparer only operates on float64. + // To handle float32s, either define a similar function for that type + // or use a Transformer to convert float32s into float64s. + opt := cmp.Comparer(func(x, y float64) bool { + delta := math.Abs(x - y) + mean := math.Abs(x+y) / 2.0 + return delta/mean < 0.00001 + }) + + x := []float64{1.0, 1.1, 1.2, math.Pi} + y := []float64{1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi + z := []float64{1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi + + fmt.Println(cmp.Equal(x, y, opt)) + fmt.Println(cmp.Equal(y, z, opt)) + fmt.Println(cmp.Equal(z, x, opt)) + + // Output: + // true + // false + // false +} + +// Normal floating-point arithmetic defines == to be false when comparing +// NaN with itself. In certain cases, this is not the desired property. +// +// This example is for demonstrative purposes; use cmpopts.EquateNaNs instead. +func ExampleOption_equalNaNs() { + // This Comparer only operates on float64. + // To handle float32s, either define a similar function for that type + // or use a Transformer to convert float32s into float64s. + opt := cmp.Comparer(func(x, y float64) bool { + return (math.IsNaN(x) && math.IsNaN(y)) || x == y + }) + + x := []float64{1.0, math.NaN(), math.E, -0.0, +0.0} + y := []float64{1.0, math.NaN(), math.E, -0.0, +0.0} + z := []float64{1.0, math.NaN(), math.Pi, -0.0, +0.0} // Pi constant instead of E + + fmt.Println(cmp.Equal(x, y, opt)) + fmt.Println(cmp.Equal(y, z, opt)) + fmt.Println(cmp.Equal(z, x, opt)) + + // Output: + // true + // false + // false +} + +// To have floating-point comparisons combine both properties of NaN being +// equal to itself and also approximate equality of values, filters are needed +// to restrict the scope of the comparison so that they are composable. +// +// This example is for demonstrative purposes; +// use cmpopts.EquateNaNs and cmpopts.EquateApprox instead. +func ExampleOption_equalNaNsAndApproximateFloats() { + alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true }) + + opts := cmp.Options{ + // This option declares that a float64 comparison is equal only if + // both inputs are NaN. + cmp.FilterValues(func(x, y float64) bool { + return math.IsNaN(x) && math.IsNaN(y) + }, alwaysEqual), + + // This option declares approximate equality on float64s only if + // both inputs are not NaN. + cmp.FilterValues(func(x, y float64) bool { + return !math.IsNaN(x) && !math.IsNaN(y) + }, cmp.Comparer(func(x, y float64) bool { + delta := math.Abs(x - y) + mean := math.Abs(x+y) / 2.0 + return delta/mean < 0.00001 + })), + } + + x := []float64{math.NaN(), 1.0, 1.1, 1.2, math.Pi} + y := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi + z := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi + + fmt.Println(cmp.Equal(x, y, opts)) + fmt.Println(cmp.Equal(y, z, opts)) + fmt.Println(cmp.Equal(z, x, opts)) + + // Output: + // true + // false + // false +} + +// Sometimes, an empty map or slice is considered equal to an allocated one +// of zero length. +// +// This example is for demonstrative purposes; use cmpopts.EquateEmpty instead. +func ExampleOption_equalEmpty() { + alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true }) + + // This option handles slices and maps of any type. + opt := cmp.FilterValues(func(x, y interface{}) bool { + vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) + return (vx.IsValid() && vy.IsValid() && vx.Type() == vy.Type()) && + (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && + (vx.Len() == 0 && vy.Len() == 0) + }, alwaysEqual) + + type S struct { + A []int + B map[string]bool + } + x := S{nil, make(map[string]bool, 100)} + y := S{make([]int, 0, 200), nil} + z := S{[]int{0}, nil} // []int has a single element (i.e., not empty) + + fmt.Println(cmp.Equal(x, y, opt)) + fmt.Println(cmp.Equal(y, z, opt)) + fmt.Println(cmp.Equal(z, x, opt)) + + // Output: + // true + // false + // false +} + +// Two slices may be considered equal if they have the same elements, +// regardless of the order that they appear in. Transformations can be used +// to sort the slice. +// +// This example is for demonstrative purposes; use cmpopts.SortSlices instead. +func ExampleOption_sortedSlice() { + // This Transformer sorts a []int. + trans := cmp.Transformer("Sort", func(in []int) []int { + out := append([]int(nil), in...) // Copy input to avoid mutating it + sort.Ints(out) + return out + }) + + x := struct{ Ints []int }{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}} + y := struct{ Ints []int }{[]int{2, 8, 0, 9, 6, 1, 4, 7, 3, 5}} + z := struct{ Ints []int }{[]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 8}} + + fmt.Println(cmp.Equal(x, y, trans)) + fmt.Println(cmp.Equal(y, z, trans)) + fmt.Println(cmp.Equal(z, x, trans)) + + // Output: + // true + // false + // false +} + +type otherString string + +func (x otherString) Equal(y otherString) bool { + return strings.ToLower(string(x)) == strings.ToLower(string(y)) +} + +// If the Equal method defined on a type is not suitable, the type can be +// dynamically transformed to be stripped of the Equal method (or any method +// for that matter). +func ExampleOption_avoidEqualMethod() { + // Suppose otherString.Equal performs a case-insensitive equality, + // which is too loose for our needs. + // We can avoid the methods of otherString by declaring a new type. + type myString otherString + + // This transformer converts otherString to myString, allowing Equal to use + // other Options to determine equality. + trans := cmp.Transformer("", func(in otherString) myString { + return myString(in) + }) + + x := []otherString{"foo", "bar", "baz"} + y := []otherString{"fOO", "bAr", "Baz"} // Same as before, but with different case + + fmt.Println(cmp.Equal(x, y)) // Equal because of case-insensitivity + fmt.Println(cmp.Equal(x, y, trans)) // Not equal because of more exact equality + + // Output: + // true + // false +} + +func roundF64(z float64) float64 { + if z < 0 { + return math.Ceil(z - 0.5) + } + return math.Floor(z + 0.5) +} + +// The complex numbers complex64 and complex128 can really just be decomposed +// into a pair of float32 or float64 values. It would be convenient to be able +// define only a single comparator on float64 and have float32, complex64, and +// complex128 all be able to use that comparator. Transformations can be used +// to handle this. +func ExampleOption_transformComplex() { + opts := []cmp.Option{ + // This transformer decomposes complex128 into a pair of float64s. + cmp.Transformer("T1", func(in complex128) (out struct{ Real, Imag float64 }) { + out.Real, out.Imag = real(in), imag(in) + return out + }), + // This transformer converts complex64 to complex128 to allow the + // above transform to take effect. + cmp.Transformer("T2", func(in complex64) complex128 { + return complex128(in) + }), + // This transformer converts float32 to float64. + cmp.Transformer("T3", func(in float32) float64 { + return float64(in) + }), + // This equality function compares float64s as rounded integers. + cmp.Comparer(func(x, y float64) bool { + return roundF64(x) == roundF64(y) + }), + } + + x := []interface{}{ + complex128(3.0), complex64(5.1 + 2.9i), float32(-1.2), float64(12.3), + } + y := []interface{}{ + complex128(3.1), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7), + } + z := []interface{}{ + complex128(3.8), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7), + } + + fmt.Println(cmp.Equal(x, y, opts...)) + fmt.Println(cmp.Equal(y, z, opts...)) + fmt.Println(cmp.Equal(z, x, opts...)) + + // Output: + // true + // false + // false +} + +type ( + Gateway struct { + SSID string + IPAddress net.IP + NetMask net.IPMask + Clients []Client + } + Client struct { + Hostname string + IPAddress net.IP + LastSeen time.Time + } +) + +func MakeGatewayInfo() (x, y Gateway) { + x = Gateway{ + SSID: "CoffeeShopWiFi", + IPAddress: net.IPv4(192, 168, 0, 1), + NetMask: net.IPv4Mask(255, 255, 0, 0), + Clients: []Client{{ + Hostname: "ristretto", + IPAddress: net.IPv4(192, 168, 0, 116), + }, { + Hostname: "aribica", + IPAddress: net.IPv4(192, 168, 0, 104), + LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC), + }, { + Hostname: "macchiato", + IPAddress: net.IPv4(192, 168, 0, 153), + LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC), + }, { + Hostname: "espresso", + IPAddress: net.IPv4(192, 168, 0, 121), + }, { + Hostname: "latte", + IPAddress: net.IPv4(192, 168, 0, 219), + LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC), + }, { + Hostname: "americano", + IPAddress: net.IPv4(192, 168, 0, 188), + LastSeen: time.Date(2009, time.November, 10, 23, 3, 5, 0, time.UTC), + }}, + } + y = Gateway{ + SSID: "CoffeeShopWiFi", + IPAddress: net.IPv4(192, 168, 0, 2), + NetMask: net.IPv4Mask(255, 255, 0, 0), + Clients: []Client{{ + Hostname: "ristretto", + IPAddress: net.IPv4(192, 168, 0, 116), + }, { + Hostname: "aribica", + IPAddress: net.IPv4(192, 168, 0, 104), + LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC), + }, { + Hostname: "macchiato", + IPAddress: net.IPv4(192, 168, 0, 153), + LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC), + }, { + Hostname: "espresso", + IPAddress: net.IPv4(192, 168, 0, 121), + }, { + Hostname: "latte", + IPAddress: net.IPv4(192, 168, 0, 221), + LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC), + }}, + } + return x, y +} + +var t fakeT + +type fakeT struct{} + +func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_panic.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_panic.go new file mode 100644 index 0000000..5ff0b42 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_panic.go @@ -0,0 +1,15 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build purego + +package cmp + +import "reflect" + +const supportExporters = false + +func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { + panic("no support for forcibly accessing unexported fields") +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_unsafe.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_unsafe.go new file mode 100644 index 0000000..21eb548 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/export_unsafe.go @@ -0,0 +1,35 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !purego + +package cmp + +import ( + "reflect" + "unsafe" +) + +const supportExporters = true + +// retrieveUnexportedField uses unsafe to forcibly retrieve any field from +// a struct such that the value has read-write permissions. +// +// The parent struct, v, must be addressable, while f must be a StructField +// describing the field to retrieve. If addr is false, +// then the returned value will be shallowed copied to be non-addressable. +func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { + ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() + if !addr { + // A field is addressable if and only if the struct is addressable. + // If the original parent value was not addressable, shallow copy the + // value to make it non-addressable to avoid leaking an implementation + // detail of how forcibly exporting a field works. + if ve.Kind() == reflect.Interface && ve.IsNil() { + return reflect.Zero(f.Type) + } + return reflect.ValueOf(ve.Interface()).Convert(f.Type) + } + return ve +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_disable.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_disable.go new file mode 100644 index 0000000..1daaaac --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_disable.go @@ -0,0 +1,17 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !cmp_debug + +package diff + +var debug debugger + +type debugger struct{} + +func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { + return f +} +func (debugger) Update() {} +func (debugger) Finish() {} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_enable.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_enable.go new file mode 100644 index 0000000..4b91dbc --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/debug_enable.go @@ -0,0 +1,122 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cmp_debug + +package diff + +import ( + "fmt" + "strings" + "sync" + "time" +) + +// The algorithm can be seen running in real-time by enabling debugging: +// go test -tags=cmp_debug -v +// +// Example output: +// === RUN TestDifference/#34 +// ┌───────────────────────────────┐ +// │ \ · · · · · · · · · · · · · · │ +// │ · # · · · · · · · · · · · · · │ +// │ · \ · · · · · · · · · · · · · │ +// │ · · \ · · · · · · · · · · · · │ +// │ · · · X # · · · · · · · · · · │ +// │ · · · # \ · · · · · · · · · · │ +// │ · · · · · # # · · · · · · · · │ +// │ · · · · · # \ · · · · · · · · │ +// │ · · · · · · · \ · · · · · · · │ +// │ · · · · · · · · \ · · · · · · │ +// │ · · · · · · · · · \ · · · · · │ +// │ · · · · · · · · · · \ · · # · │ +// │ · · · · · · · · · · · \ # # · │ +// │ · · · · · · · · · · · # # # · │ +// │ · · · · · · · · · · # # # # · │ +// │ · · · · · · · · · # # # # # · │ +// │ · · · · · · · · · · · · · · \ │ +// └───────────────────────────────┘ +// [.Y..M.XY......YXYXY.|] +// +// The grid represents the edit-graph where the horizontal axis represents +// list X and the vertical axis represents list Y. The start of the two lists +// is the top-left, while the ends are the bottom-right. The '·' represents +// an unexplored node in the graph. The '\' indicates that the two symbols +// from list X and Y are equal. The 'X' indicates that two symbols are similar +// (but not exactly equal) to each other. The '#' indicates that the two symbols +// are different (and not similar). The algorithm traverses this graph trying to +// make the paths starting in the top-left and the bottom-right connect. +// +// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents +// the currently established path from the forward and reverse searches, +// separated by a '|' character. + +const ( + updateDelay = 100 * time.Millisecond + finishDelay = 500 * time.Millisecond + ansiTerminal = true // ANSI escape codes used to move terminal cursor +) + +var debug debugger + +type debugger struct { + sync.Mutex + p1, p2 EditScript + fwdPath, revPath *EditScript + grid []byte + lines int +} + +func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { + dbg.Lock() + dbg.fwdPath, dbg.revPath = p1, p2 + top := "┌─" + strings.Repeat("──", nx) + "┐\n" + row := "│ " + strings.Repeat("· ", nx) + "│\n" + btm := "└─" + strings.Repeat("──", nx) + "┘\n" + dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) + dbg.lines = strings.Count(dbg.String(), "\n") + fmt.Print(dbg) + + // Wrap the EqualFunc so that we can intercept each result. + return func(ix, iy int) (r Result) { + cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] + for i := range cell { + cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot + } + switch r = f(ix, iy); { + case r.Equal(): + cell[0] = '\\' + case r.Similar(): + cell[0] = 'X' + default: + cell[0] = '#' + } + return + } +} + +func (dbg *debugger) Update() { + dbg.print(updateDelay) +} + +func (dbg *debugger) Finish() { + dbg.print(finishDelay) + dbg.Unlock() +} + +func (dbg *debugger) String() string { + dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] + for i := len(*dbg.revPath) - 1; i >= 0; i-- { + dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) + } + return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) +} + +func (dbg *debugger) print(d time.Duration) { + if ansiTerminal { + fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor + } + fmt.Print(dbg) + time.Sleep(d) +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff.go new file mode 100644 index 0000000..bc196b1 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff.go @@ -0,0 +1,398 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package diff implements an algorithm for producing edit-scripts. +// The edit-script is a sequence of operations needed to transform one list +// of symbols into another (or vice-versa). The edits allowed are insertions, +// deletions, and modifications. The summation of all edits is called the +// Levenshtein distance as this problem is well-known in computer science. +// +// This package prioritizes performance over accuracy. That is, the run time +// is more important than obtaining a minimal Levenshtein distance. +package diff + +import ( + "math/rand" + "time" + + "github.com/google/go-cmp/cmp/internal/flags" +) + +// EditType represents a single operation within an edit-script. +type EditType uint8 + +const ( + // Identity indicates that a symbol pair is identical in both list X and Y. + Identity EditType = iota + // UniqueX indicates that a symbol only exists in X and not Y. + UniqueX + // UniqueY indicates that a symbol only exists in Y and not X. + UniqueY + // Modified indicates that a symbol pair is a modification of each other. + Modified +) + +// EditScript represents the series of differences between two lists. +type EditScript []EditType + +// String returns a human-readable string representing the edit-script where +// Identity, UniqueX, UniqueY, and Modified are represented by the +// '.', 'X', 'Y', and 'M' characters, respectively. +func (es EditScript) String() string { + b := make([]byte, len(es)) + for i, e := range es { + switch e { + case Identity: + b[i] = '.' + case UniqueX: + b[i] = 'X' + case UniqueY: + b[i] = 'Y' + case Modified: + b[i] = 'M' + default: + panic("invalid edit-type") + } + } + return string(b) +} + +// stats returns a histogram of the number of each type of edit operation. +func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) { + for _, e := range es { + switch e { + case Identity: + s.NI++ + case UniqueX: + s.NX++ + case UniqueY: + s.NY++ + case Modified: + s.NM++ + default: + panic("invalid edit-type") + } + } + return +} + +// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if +// lists X and Y are equal. +func (es EditScript) Dist() int { return len(es) - es.stats().NI } + +// LenX is the length of the X list. +func (es EditScript) LenX() int { return len(es) - es.stats().NY } + +// LenY is the length of the Y list. +func (es EditScript) LenY() int { return len(es) - es.stats().NX } + +// EqualFunc reports whether the symbols at indexes ix and iy are equal. +// When called by Difference, the index is guaranteed to be within nx and ny. +type EqualFunc func(ix int, iy int) Result + +// Result is the result of comparison. +// NumSame is the number of sub-elements that are equal. +// NumDiff is the number of sub-elements that are not equal. +type Result struct{ NumSame, NumDiff int } + +// BoolResult returns a Result that is either Equal or not Equal. +func BoolResult(b bool) Result { + if b { + return Result{NumSame: 1} // Equal, Similar + } else { + return Result{NumDiff: 2} // Not Equal, not Similar + } +} + +// Equal indicates whether the symbols are equal. Two symbols are equal +// if and only if NumDiff == 0. If Equal, then they are also Similar. +func (r Result) Equal() bool { return r.NumDiff == 0 } + +// Similar indicates whether two symbols are similar and may be represented +// by using the Modified type. As a special case, we consider binary comparisons +// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. +// +// The exact ratio of NumSame to NumDiff to determine similarity may change. +func (r Result) Similar() bool { + // Use NumSame+1 to offset NumSame so that binary comparisons are similar. + return r.NumSame+1 >= r.NumDiff +} + +var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 + +// Difference reports whether two lists of lengths nx and ny are equal +// given the definition of equality provided as f. +// +// This function returns an edit-script, which is a sequence of operations +// needed to convert one list into the other. The following invariants for +// the edit-script are maintained: +// • eq == (es.Dist()==0) +// • nx == es.LenX() +// • ny == es.LenY() +// +// This algorithm is not guaranteed to be an optimal solution (i.e., one that +// produces an edit-script with a minimal Levenshtein distance). This algorithm +// favors performance over optimality. The exact output is not guaranteed to +// be stable and may change over time. +func Difference(nx, ny int, f EqualFunc) (es EditScript) { + // This algorithm is based on traversing what is known as an "edit-graph". + // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" + // by Eugene W. Myers. Since D can be as large as N itself, this is + // effectively O(N^2). Unlike the algorithm from that paper, we are not + // interested in the optimal path, but at least some "decent" path. + // + // For example, let X and Y be lists of symbols: + // X = [A B C A B B A] + // Y = [C B A B A C] + // + // The edit-graph can be drawn as the following: + // A B C A B B A + // ┌─────────────┐ + // C │_|_|\|_|_|_|_│ 0 + // B │_|\|_|_|\|\|_│ 1 + // A │\|_|_|\|_|_|\│ 2 + // B │_|\|_|_|\|\|_│ 3 + // A │\|_|_|\|_|_|\│ 4 + // C │ | |\| | | | │ 5 + // └─────────────┘ 6 + // 0 1 2 3 4 5 6 7 + // + // List X is written along the horizontal axis, while list Y is written + // along the vertical axis. At any point on this grid, if the symbol in + // list X matches the corresponding symbol in list Y, then a '\' is drawn. + // The goal of any minimal edit-script algorithm is to find a path from the + // top-left corner to the bottom-right corner, while traveling through the + // fewest horizontal or vertical edges. + // A horizontal edge is equivalent to inserting a symbol from list X. + // A vertical edge is equivalent to inserting a symbol from list Y. + // A diagonal edge is equivalent to a matching symbol between both X and Y. + + // Invariants: + // • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx + // • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny + // + // In general: + // • fwdFrontier.X < revFrontier.X + // • fwdFrontier.Y < revFrontier.Y + // Unless, it is time for the algorithm to terminate. + fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} + revPath := path{-1, point{nx, ny}, make(EditScript, 0)} + fwdFrontier := fwdPath.point // Forward search frontier + revFrontier := revPath.point // Reverse search frontier + + // Search budget bounds the cost of searching for better paths. + // The longest sequence of non-matching symbols that can be tolerated is + // approximately the square-root of the search budget. + searchBudget := 4 * (nx + ny) // O(n) + + // Running the tests with the "cmp_debug" build tag prints a visualization + // of the algorithm running in real-time. This is educational for + // understanding how the algorithm works. See debug_enable.go. + f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) + + // The algorithm below is a greedy, meet-in-the-middle algorithm for + // computing sub-optimal edit-scripts between two lists. + // + // The algorithm is approximately as follows: + // • Searching for differences switches back-and-forth between + // a search that starts at the beginning (the top-left corner), and + // a search that starts at the end (the bottom-right corner). The goal of + // the search is connect with the search from the opposite corner. + // • As we search, we build a path in a greedy manner, where the first + // match seen is added to the path (this is sub-optimal, but provides a + // decent result in practice). When matches are found, we try the next pair + // of symbols in the lists and follow all matches as far as possible. + // • When searching for matches, we search along a diagonal going through + // through the "frontier" point. If no matches are found, we advance the + // frontier towards the opposite corner. + // • This algorithm terminates when either the X coordinates or the + // Y coordinates of the forward and reverse frontier points ever intersect. + + // This algorithm is correct even if searching only in the forward direction + // or in the reverse direction. We do both because it is commonly observed + // that two lists commonly differ because elements were added to the front + // or end of the other list. + // + // Non-deterministically start with either the forward or reverse direction + // to introduce some deliberate instability so that we have the flexibility + // to change this algorithm in the future. + if flags.Deterministic || randBool { + goto forwardSearch + } else { + goto reverseSearch + } + +forwardSearch: + { + // Forward search from the beginning. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + goto finishSearch + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{fwdFrontier.X + z, fwdFrontier.Y - z} + switch { + case p.X >= revPath.X || p.Y < fwdPath.Y: + stop1 = true // Hit top-right corner + case p.Y >= revPath.Y || p.X < fwdPath.X: + stop2 = true // Hit bottom-left corner + case f(p.X, p.Y).Equal(): + // Match found, so connect the path to this point. + fwdPath.connect(p, f) + fwdPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(fwdPath.X, fwdPath.Y).Equal() { + break + } + fwdPath.append(Identity) + } + fwdFrontier = fwdPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards reverse point. + if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y { + fwdFrontier.X++ + } else { + fwdFrontier.Y++ + } + goto reverseSearch + } + +reverseSearch: + { + // Reverse search from the end. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + goto finishSearch + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{revFrontier.X - z, revFrontier.Y + z} + switch { + case fwdPath.X >= p.X || revPath.Y < p.Y: + stop1 = true // Hit bottom-left corner + case fwdPath.Y >= p.Y || revPath.X < p.X: + stop2 = true // Hit top-right corner + case f(p.X-1, p.Y-1).Equal(): + // Match found, so connect the path to this point. + revPath.connect(p, f) + revPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(revPath.X-1, revPath.Y-1).Equal() { + break + } + revPath.append(Identity) + } + revFrontier = revPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards forward point. + if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y { + revFrontier.X-- + } else { + revFrontier.Y-- + } + goto forwardSearch + } + +finishSearch: + // Join the forward and reverse paths and then append the reverse path. + fwdPath.connect(revPath.point, f) + for i := len(revPath.es) - 1; i >= 0; i-- { + t := revPath.es[i] + revPath.es = revPath.es[:i] + fwdPath.append(t) + } + debug.Finish() + return fwdPath.es +} + +type path struct { + dir int // +1 if forward, -1 if reverse + point // Leading point of the EditScript path + es EditScript +} + +// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types +// to the edit-script to connect p.point to dst. +func (p *path) connect(dst point, f EqualFunc) { + if p.dir > 0 { + // Connect in forward direction. + for dst.X > p.X && dst.Y > p.Y { + switch r := f(p.X, p.Y); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case dst.X-p.X >= dst.Y-p.Y: + p.append(UniqueX) + default: + p.append(UniqueY) + } + } + for dst.X > p.X { + p.append(UniqueX) + } + for dst.Y > p.Y { + p.append(UniqueY) + } + } else { + // Connect in reverse direction. + for p.X > dst.X && p.Y > dst.Y { + switch r := f(p.X-1, p.Y-1); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case p.Y-dst.Y >= p.X-dst.X: + p.append(UniqueY) + default: + p.append(UniqueX) + } + } + for p.X > dst.X { + p.append(UniqueX) + } + for p.Y > dst.Y { + p.append(UniqueY) + } + } +} + +func (p *path) append(t EditType) { + p.es = append(p.es, t) + switch t { + case Identity, Modified: + p.add(p.dir, p.dir) + case UniqueX: + p.add(p.dir, 0) + case UniqueY: + p.add(0, p.dir) + } + debug.Update() +} + +type point struct{ X, Y int } + +func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } + +// zigzag maps a consecutive sequence of integers to a zig-zag sequence. +// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] +func zigzag(x int) int { + if x&1 != 0 { + x = ^x + } + return x >> 1 +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff_test.go new file mode 100644 index 0000000..eacf072 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/diff/diff_test.go @@ -0,0 +1,449 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package diff + +import ( + "fmt" + "math/rand" + "strings" + "testing" + "unicode" +) + +func TestDifference(t *testing.T) { + tests := []struct { + // Before passing x and y to Difference, we strip all spaces so that + // they can be used by the test author to indicate a missing symbol + // in one of the lists. + x, y string + want string // '|' separated list of possible outputs + }{{ + x: "", + y: "", + want: "", + }, { + x: "#", + y: "#", + want: ".", + }, { + x: "##", + y: "# ", + want: ".X|X.", + }, { + x: "a#", + y: "A ", + want: "MX", + }, { + x: "#a", + y: " A", + want: "XM", + }, { + x: "# ", + y: "##", + want: ".Y|Y.", + }, { + x: " #", + y: "@#", + want: "Y.", + }, { + x: "@#", + y: " #", + want: "X.", + }, { + x: "##########0123456789", + y: " 0123456789", + want: "XXXXXXXXXX..........", + }, { + x: " 0123456789", + y: "##########0123456789", + want: "YYYYYYYYYY..........", + }, { + x: "#####0123456789#####", + y: " 0123456789 ", + want: "XXXXX..........XXXXX", + }, { + x: " 0123456789 ", + y: "#####0123456789#####", + want: "YYYYY..........YYYYY", + }, { + x: "01234##########56789", + y: "01234 56789", + want: ".....XXXXXXXXXX.....", + }, { + x: "01234 56789", + y: "01234##########56789", + want: ".....YYYYYYYYYY.....", + }, { + x: "0123456789##########", + y: "0123456789 ", + want: "..........XXXXXXXXXX", + }, { + x: "0123456789 ", + y: "0123456789##########", + want: "..........YYYYYYYYYY", + }, { + x: "abcdefghij0123456789", + y: "ABCDEFGHIJ0123456789", + want: "MMMMMMMMMM..........", + }, { + x: "ABCDEFGHIJ0123456789", + y: "abcdefghij0123456789", + want: "MMMMMMMMMM..........", + }, { + x: "01234abcdefghij56789", + y: "01234ABCDEFGHIJ56789", + want: ".....MMMMMMMMMM.....", + }, { + x: "01234ABCDEFGHIJ56789", + y: "01234abcdefghij56789", + want: ".....MMMMMMMMMM.....", + }, { + x: "0123456789abcdefghij", + y: "0123456789ABCDEFGHIJ", + want: "..........MMMMMMMMMM", + }, { + x: "0123456789ABCDEFGHIJ", + y: "0123456789abcdefghij", + want: "..........MMMMMMMMMM", + }, { + x: "ABCDEFGHIJ0123456789 ", + y: " 0123456789abcdefghij", + want: "XXXXXXXXXX..........YYYYYYYYYY", + }, { + x: " 0123456789abcdefghij", + y: "ABCDEFGHIJ0123456789 ", + want: "YYYYYYYYYY..........XXXXXXXXXX", + }, { + x: "ABCDE0123456789 FGHIJ", + y: " 0123456789abcdefghij", + want: "XXXXX..........YYYYYMMMMM", + }, { + x: " 0123456789abcdefghij", + y: "ABCDE0123456789 FGHIJ", + want: "YYYYY..........XXXXXMMMMM", + }, { + x: "ABCDE01234F G H I J 56789 ", + y: " 01234 a b c d e56789fghij", + want: "XXXXX.....XYXYXYXYXY.....YYYYY", + }, { + x: " 01234a b c d e 56789fghij", + y: "ABCDE01234 F G H I J56789 ", + want: "YYYYY.....XYXYXYXYXY.....XXXXX", + }, { + x: "FGHIJ01234ABCDE56789 ", + y: " 01234abcde56789fghij", + want: "XXXXX.....MMMMM.....YYYYY", + }, { + x: " 01234abcde56789fghij", + y: "FGHIJ01234ABCDE56789 ", + want: "YYYYY.....MMMMM.....XXXXX", + }, { + x: "ABCAB BA ", + y: " C BABAC", + want: "XX.X.Y..Y|XX.Y.X..Y", + }, { + x: "# #### ###", + y: "#y####yy###", + want: ".Y....YY...", + }, { + x: "# #### # ##x#x", + y: "#y####y y## # ", + want: ".Y....YXY..X.X", + }, { + x: "###z#z###### x #", + y: "#y##Z#Z###### yy#", + want: ".Y..M.M......XYY.", + }, { + x: "0 12z3x 456789 x x 0", + y: "0y12Z3 y456789y y y0", + want: ".Y..M.XY......YXYXY.|.Y..M.XY......XYXYY.", + }, { + x: "0 2 4 6 8 ..................abXXcdEXF.ghXi", + y: " 1 3 5 7 9..................AB CDE F.GH I", + want: "XYXYXYXYXY..................MMXXMM.X..MMXM", + }, { + x: "I HG.F EDC BA..................9 7 5 3 1 ", + y: "iXhg.FXEdcXXba.................. 8 6 4 2 0", + want: "MYMM..Y.MMYYMM..................XYXYXYXYXY", + }, { + x: "x1234", + y: " 1234", + want: "X....", + }, { + x: "x123x4", + y: " 123 4", + want: "X...X.", + }, { + x: "x1234x56", + y: " 1234 ", + want: "X....XXX", + }, { + x: "x1234xxx56", + y: " 1234 56", + want: "X....XXX..", + }, { + x: ".1234...ab", + y: " 1234 AB", + want: "X....XXXMM", + }, { + x: "x1234xxab.", + y: " 1234 AB ", + want: "X....XXMMX", + }, { + x: " 0123456789", + y: "9012345678 ", + want: "Y.........X", + }, { + x: " 0123456789", + y: "8901234567 ", + want: "YY........XX", + }, { + x: " 0123456789", + y: "7890123456 ", + want: "YYY.......XXX", + }, { + x: " 0123456789", + y: "6789012345 ", + want: "YYYY......XXXX", + }, { + x: "0123456789 ", + y: " 5678901234", + want: "XXXXX.....YYYYY|YYYYY.....XXXXX", + }, { + x: "0123456789 ", + y: " 4567890123", + want: "XXXX......YYYY", + }, { + x: "0123456789 ", + y: " 3456789012", + want: "XXX.......YYY", + }, { + x: "0123456789 ", + y: " 2345678901", + want: "XX........YY", + }, { + x: "0123456789 ", + y: " 1234567890", + want: "X.........Y", + }, { + x: "0 1 2 3 45 6 7 8 9 ", + y: " 9 8 7 6 54 3 2 1 0", + want: "XYXYXYXYX.YXYXYXYXY", + }, { + x: "0 1 2345678 9 ", + y: " 6 72 5 819034", + want: "XYXY.XX.XX.Y.YYY", + }, { + x: "F B Q M O I G T L N72X90 E 4S P 651HKRJU DA 83CVZW", + y: " 5 W H XO10R9IV K ZLCTAJ8P3N SEQM4 7 2G6 UBD F ", + want: "XYXYXYXY.YYYY.YXYXY.YYYYYYY.XXXXXY.YY.XYXYY.XXXXXX.Y.XYXXXXXX", + }} + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + x := strings.Replace(tt.x, " ", "", -1) + y := strings.Replace(tt.y, " ", "", -1) + es := testStrings(t, x, y) + var want string + got := es.String() + for _, want = range strings.Split(tt.want, "|") { + if got == want { + return + } + } + t.Errorf("Difference(%s, %s):\ngot %s\nwant %s", x, y, got, want) + }) + } +} + +func TestDifferenceFuzz(t *testing.T) { + tests := []struct{ px, py, pm float32 }{ + {px: 0.0, py: 0.0, pm: 0.1}, + {px: 0.0, py: 0.1, pm: 0.0}, + {px: 0.1, py: 0.0, pm: 0.0}, + {px: 0.0, py: 0.1, pm: 0.1}, + {px: 0.1, py: 0.0, pm: 0.1}, + {px: 0.2, py: 0.2, pm: 0.2}, + {px: 0.3, py: 0.1, pm: 0.2}, + {px: 0.1, py: 0.3, pm: 0.2}, + {px: 0.2, py: 0.2, pm: 0.2}, + {px: 0.3, py: 0.3, pm: 0.3}, + {px: 0.1, py: 0.1, pm: 0.5}, + {px: 0.4, py: 0.1, pm: 0.5}, + {px: 0.3, py: 0.2, pm: 0.5}, + {px: 0.2, py: 0.3, pm: 0.5}, + {px: 0.1, py: 0.4, pm: 0.5}, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("P%d", i), func(t *testing.T) { + // Sweep from 1B to 1KiB. + for n := 1; n <= 1024; n <<= 1 { + t.Run(fmt.Sprintf("N%d", n), func(t *testing.T) { + for j := 0; j < 10; j++ { + x, y := generateStrings(n, tt.px, tt.py, tt.pm, int64(j)) + testStrings(t, x, y) + } + }) + } + }) + } +} + +func BenchmarkDifference(b *testing.B) { + for n := 1 << 10; n <= 1<<20; n <<= 2 { + b.Run(fmt.Sprintf("N%d", n), func(b *testing.B) { + x, y := generateStrings(n, 0.05, 0.05, 0.10, 0) + b.ReportAllocs() + b.SetBytes(int64(len(x) + len(y))) + for i := 0; i < b.N; i++ { + Difference(len(x), len(y), func(ix, iy int) Result { + return compareByte(x[ix], y[iy]) + }) + } + }) + } +} + +func generateStrings(n int, px, py, pm float32, seed int64) (string, string) { + if px+py+pm > 1.0 { + panic("invalid probabilities") + } + py += px + pm += py + + b := make([]byte, n) + r := rand.New(rand.NewSource(seed)) + r.Read(b) + + var x, y []byte + for len(b) > 0 { + switch p := r.Float32(); { + case p < px: // UniqueX + x = append(x, b[0]) + case p < py: // UniqueY + y = append(y, b[0]) + case p < pm: // Modified + x = append(x, 'A'+(b[0]%26)) + y = append(y, 'a'+(b[0]%26)) + default: // Identity + x = append(x, b[0]) + y = append(y, b[0]) + } + b = b[1:] + } + return string(x), string(y) +} + +func testStrings(t *testing.T, x, y string) EditScript { + es := Difference(len(x), len(y), func(ix, iy int) Result { + return compareByte(x[ix], y[iy]) + }) + if es.LenX() != len(x) { + t.Errorf("es.LenX = %d, want %d", es.LenX(), len(x)) + } + if es.LenY() != len(y) { + t.Errorf("es.LenY = %d, want %d", es.LenY(), len(y)) + } + if !validateScript(x, y, es) { + t.Errorf("invalid edit script: %v", es) + } + return es +} + +func validateScript(x, y string, es EditScript) bool { + var bx, by []byte + for _, e := range es { + switch e { + case Identity: + if !compareByte(x[len(bx)], y[len(by)]).Equal() { + return false + } + bx = append(bx, x[len(bx)]) + by = append(by, y[len(by)]) + case UniqueX: + bx = append(bx, x[len(bx)]) + case UniqueY: + by = append(by, y[len(by)]) + case Modified: + if !compareByte(x[len(bx)], y[len(by)]).Similar() { + return false + } + bx = append(bx, x[len(bx)]) + by = append(by, y[len(by)]) + } + } + return string(bx) == x && string(by) == y +} + +// compareByte returns a Result where the result is Equal if x == y, +// similar if x and y differ only in casing, and different otherwise. +func compareByte(x, y byte) (r Result) { + switch { + case x == y: + return equalResult // Identity + case unicode.ToUpper(rune(x)) == unicode.ToUpper(rune(y)): + return similarResult // Modified + default: + return differentResult // UniqueX or UniqueY + } +} + +var ( + equalResult = Result{NumDiff: 0} + similarResult = Result{NumDiff: 1} + differentResult = Result{NumDiff: 2} +) + +func TestResult(t *testing.T) { + tests := []struct { + result Result + wantEqual bool + wantSimilar bool + }{ + // equalResult is equal since NumDiff == 0, by definition of Equal method. + {equalResult, true, true}, + // similarResult is similar since it is a binary result where only one + // element was compared (i.e., Either NumSame==1 or NumDiff==1). + {similarResult, false, true}, + // differentResult is different since there are enough differences that + // it isn't even considered similar. + {differentResult, false, false}, + + // Zero value is always equal. + {Result{NumSame: 0, NumDiff: 0}, true, true}, + + // Binary comparisons (where NumSame+NumDiff == 1) are always similar. + {Result{NumSame: 1, NumDiff: 0}, true, true}, + {Result{NumSame: 0, NumDiff: 1}, false, true}, + + // More complex ratios. The exact ratio for similarity may change, + // and may require updates to these test cases. + {Result{NumSame: 1, NumDiff: 1}, false, true}, + {Result{NumSame: 1, NumDiff: 2}, false, true}, + {Result{NumSame: 1, NumDiff: 3}, false, false}, + {Result{NumSame: 2, NumDiff: 1}, false, true}, + {Result{NumSame: 2, NumDiff: 2}, false, true}, + {Result{NumSame: 2, NumDiff: 3}, false, true}, + {Result{NumSame: 3, NumDiff: 1}, false, true}, + {Result{NumSame: 3, NumDiff: 2}, false, true}, + {Result{NumSame: 3, NumDiff: 3}, false, true}, + {Result{NumSame: 1000, NumDiff: 0}, true, true}, + {Result{NumSame: 1000, NumDiff: 1}, false, true}, + {Result{NumSame: 1000, NumDiff: 2}, false, true}, + {Result{NumSame: 0, NumDiff: 1000}, false, false}, + {Result{NumSame: 1, NumDiff: 1000}, false, false}, + {Result{NumSame: 2, NumDiff: 1000}, false, false}, + } + + for _, tt := range tests { + if got := tt.result.Equal(); got != tt.wantEqual { + t.Errorf("%#v.Equal() = %v, want %v", tt.result, got, tt.wantEqual) + } + if got := tt.result.Similar(); got != tt.wantSimilar { + t.Errorf("%#v.Similar() = %v, want %v", tt.result, got, tt.wantSimilar) + } + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/flags.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/flags.go new file mode 100644 index 0000000..d8e459c --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/flags.go @@ -0,0 +1,9 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flags + +// Deterministic controls whether the output of Diff should be deterministic. +// This is only used for testing. +var Deterministic bool diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_legacy.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_legacy.go new file mode 100644 index 0000000..82d1d7f --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_legacy.go @@ -0,0 +1,10 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.10 + +package flags + +// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. +const AtLeastGo110 = false diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_recent.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_recent.go new file mode 100644 index 0000000..8646f05 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/flags/toolchain_recent.go @@ -0,0 +1,10 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.10 + +package flags + +// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. +const AtLeastGo110 = true diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func.go new file mode 100644 index 0000000..d127d43 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func.go @@ -0,0 +1,99 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package function provides functionality for identifying function types. +package function + +import ( + "reflect" + "regexp" + "runtime" + "strings" +) + +type funcType int + +const ( + _ funcType = iota + + tbFunc // func(T) bool + ttbFunc // func(T, T) bool + trbFunc // func(T, R) bool + tibFunc // func(T, I) bool + trFunc // func(T) R + + Equal = ttbFunc // func(T, T) bool + EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool + Transformer = trFunc // func(T) R + ValueFilter = ttbFunc // func(T, T) bool + Less = ttbFunc // func(T, T) bool + ValuePredicate = tbFunc // func(T) bool + KeyValuePredicate = trbFunc // func(T, R) bool +) + +var boolType = reflect.TypeOf(true) + +// IsType reports whether the reflect.Type is of the specified function type. +func IsType(t reflect.Type, ft funcType) bool { + if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { + return false + } + ni, no := t.NumIn(), t.NumOut() + switch ft { + case tbFunc: // func(T) bool + if ni == 1 && no == 1 && t.Out(0) == boolType { + return true + } + case ttbFunc: // func(T, T) bool + if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { + return true + } + case trbFunc: // func(T, R) bool + if ni == 2 && no == 1 && t.Out(0) == boolType { + return true + } + case tibFunc: // func(T, I) bool + if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { + return true + } + case trFunc: // func(T) R + if ni == 1 && no == 1 { + return true + } + } + return false +} + +var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) + +// NameOf returns the name of the function value. +func NameOf(v reflect.Value) string { + fnc := runtime.FuncForPC(v.Pointer()) + if fnc == nil { + return "" + } + fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" + + // Method closures have a "-fm" suffix. + fullName = strings.TrimSuffix(fullName, "-fm") + + var name string + for len(fullName) > 0 { + inParen := strings.HasSuffix(fullName, ")") + fullName = strings.TrimSuffix(fullName, ")") + + s := lastIdentRx.FindString(fullName) + if s == "" { + break + } + name = s + "." + name + fullName = strings.TrimSuffix(fullName, s) + + if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { + fullName = fullName[:i] + } + fullName = strings.TrimSuffix(fullName, ".") + } + return strings.TrimSuffix(name, ".") +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func_test.go new file mode 100644 index 0000000..f03ef45 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/function/func_test.go @@ -0,0 +1,51 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package function + +import ( + "bytes" + "reflect" + "testing" +) + +type myType struct{ bytes.Buffer } + +func (myType) valueMethod() {} +func (myType) ValueMethod() {} + +func (*myType) pointerMethod() {} +func (*myType) PointerMethod() {} + +func TestNameOf(t *testing.T) { + tests := []struct { + fnc interface{} + want string + }{ + {TestNameOf, "function.TestNameOf"}, + {func() {}, "function.TestNameOf.func1"}, + {(myType).valueMethod, "function.myType.valueMethod"}, + {(myType).ValueMethod, "function.myType.ValueMethod"}, + {(myType{}).valueMethod, "function.myType.valueMethod"}, + {(myType{}).ValueMethod, "function.myType.ValueMethod"}, + {(*myType).valueMethod, "function.myType.valueMethod"}, + {(*myType).ValueMethod, "function.myType.ValueMethod"}, + {(&myType{}).valueMethod, "function.myType.valueMethod"}, + {(&myType{}).ValueMethod, "function.myType.ValueMethod"}, + {(*myType).pointerMethod, "function.myType.pointerMethod"}, + {(*myType).PointerMethod, "function.myType.PointerMethod"}, + {(&myType{}).pointerMethod, "function.myType.pointerMethod"}, + {(&myType{}).PointerMethod, "function.myType.PointerMethod"}, + {(*myType).Write, "function.myType.Write"}, + {(&myType{}).Write, "bytes.Buffer.Write"}, + } + for _, tt := range tests { + t.Run("", func(t *testing.T) { + got := NameOf(reflect.ValueOf(tt.fnc)) + if got != tt.want { + t.Errorf("NameOf() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/testprotos/protos.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/testprotos/protos.go new file mode 100644 index 0000000..81622d3 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/testprotos/protos.go @@ -0,0 +1,116 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testprotos + +func Equal(x, y Message) bool { + if x == nil || y == nil { + return x == nil && y == nil + } + return x.String() == y.String() +} + +type Message interface { + Proto() + String() string +} + +type proto interface { + Proto() +} + +type notComparable struct { + unexportedField func() +} + +type Stringer struct{ X string } + +func (s *Stringer) String() string { return s.X } + +// Project1 protocol buffers +type ( + Eagle_States int + Eagle_MissingCalls int + Dreamer_States int + Dreamer_MissingCalls int + Slap_States int + Goat_States int + Donkey_States int + SummerType int + + Eagle struct { + proto + notComparable + Stringer + } + Dreamer struct { + proto + notComparable + Stringer + } + Slap struct { + proto + notComparable + Stringer + } + Goat struct { + proto + notComparable + Stringer + } + Donkey struct { + proto + notComparable + Stringer + } +) + +// Project2 protocol buffers +type ( + Germ struct { + proto + notComparable + Stringer + } + Dish struct { + proto + notComparable + Stringer + } +) + +// Project3 protocol buffers +type ( + Dirt struct { + proto + notComparable + Stringer + } + Wizard struct { + proto + notComparable + Stringer + } + Sadistic struct { + proto + notComparable + Stringer + } +) + +// Project4 protocol buffers +type ( + HoneyStatus int + PoisonType int + MetaData struct { + proto + notComparable + Stringer + } + Restrictions struct { + proto + notComparable + Stringer + } +) diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo1/foo.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo1/foo.go new file mode 100644 index 0000000..c0882fb --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo1/foo.go @@ -0,0 +1,10 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package foo is deliberately named differently than the parent directory. +// It contain declarations that have ambiguity in their short names, +// relative to a different package also called foo. +package foo + +type Bar struct{ S string } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo2/foo.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo2/foo.go new file mode 100644 index 0000000..c0882fb --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/foo2/foo.go @@ -0,0 +1,10 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package foo is deliberately named differently than the parent directory. +// It contain declarations that have ambiguity in their short names, +// relative to a different package also called foo. +package foo + +type Bar struct{ S string } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project1.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project1.go new file mode 100644 index 0000000..223d6ab --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project1.go @@ -0,0 +1,267 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package teststructs + +import ( + "time" + + pb "github.com/google/go-cmp/cmp/internal/testprotos" +) + +// This is an sanitized example of equality from a real use-case. +// The original equality function was as follows: +/* +func equalEagle(x, y Eagle) bool { + if x.Name != y.Name && + !reflect.DeepEqual(x.Hounds, y.Hounds) && + x.Desc != y.Desc && + x.DescLong != y.DescLong && + x.Prong != y.Prong && + x.StateGoverner != y.StateGoverner && + x.PrankRating != y.PrankRating && + x.FunnyPrank != y.FunnyPrank && + !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) { + return false + } + + if len(x.Dreamers) != len(y.Dreamers) { + return false + } + for i := range x.Dreamers { + if !equalDreamer(x.Dreamers[i], y.Dreamers[i]) { + return false + } + } + if len(x.Slaps) != len(y.Slaps) { + return false + } + for i := range x.Slaps { + if !equalSlap(x.Slaps[i], y.Slaps[i]) { + return false + } + } + return true +} +func equalDreamer(x, y Dreamer) bool { + if x.Name != y.Name || + x.Desc != y.Desc || + x.DescLong != y.DescLong || + x.ContSlapsInterval != y.ContSlapsInterval || + x.Ornamental != y.Ornamental || + x.Amoeba != y.Amoeba || + x.Heroes != y.Heroes || + x.FloppyDisk != y.FloppyDisk || + x.MightiestDuck != y.MightiestDuck || + x.FunnyPrank != y.FunnyPrank || + !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) { + + return false + } + if len(x.Animal) != len(y.Animal) { + return false + } + for i := range x.Animal { + vx := x.Animal[i] + vy := y.Animal[i] + if reflect.TypeOf(x.Animal) != reflect.TypeOf(y.Animal) { + return false + } + switch vx.(type) { + case Goat: + if !equalGoat(vx.(Goat), vy.(Goat)) { + return false + } + case Donkey: + if !equalDonkey(vx.(Donkey), vy.(Donkey)) { + return false + } + default: + panic(fmt.Sprintf("unknown type: %T", vx)) + } + } + if len(x.PreSlaps) != len(y.PreSlaps) { + return false + } + for i := range x.PreSlaps { + if !equalSlap(x.PreSlaps[i], y.PreSlaps[i]) { + return false + } + } + if len(x.ContSlaps) != len(y.ContSlaps) { + return false + } + for i := range x.ContSlaps { + if !equalSlap(x.ContSlaps[i], y.ContSlaps[i]) { + return false + } + } + return true +} +func equalSlap(x, y Slap) bool { + return x.Name == y.Name && + x.Desc == y.Desc && + x.DescLong == y.DescLong && + pb.Equal(x.Args, y.Args) && + x.Tense == y.Tense && + x.Interval == y.Interval && + x.Homeland == y.Homeland && + x.FunnyPrank == y.FunnyPrank && + pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) +} +func equalGoat(x, y Goat) bool { + if x.Target != y.Target || + x.FunnyPrank != y.FunnyPrank || + !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) { + return false + } + if len(x.Slaps) != len(y.Slaps) { + return false + } + for i := range x.Slaps { + if !equalSlap(x.Slaps[i], y.Slaps[i]) { + return false + } + } + return true +} +func equalDonkey(x, y Donkey) bool { + return x.Pause == y.Pause && + x.Sleep == y.Sleep && + x.FunnyPrank == y.FunnyPrank && + pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) +} +*/ + +type Eagle struct { + Name string + Hounds []string + Desc string + DescLong string + Dreamers []Dreamer + Prong int64 + Slaps []Slap + StateGoverner string + PrankRating string + FunnyPrank string + Immutable *EagleImmutable +} + +type EagleImmutable struct { + ID string + State *pb.Eagle_States + MissingCall *pb.Eagle_MissingCalls + Birthday time.Time + Death time.Time + Started time.Time + LastUpdate time.Time + Creator string + empty bool +} + +type Dreamer struct { + Name string + Desc string + DescLong string + PreSlaps []Slap + ContSlaps []Slap + ContSlapsInterval int32 + Animal []interface{} // Could be either Goat or Donkey + Ornamental bool + Amoeba int64 + Heroes int32 + FloppyDisk int32 + MightiestDuck bool + FunnyPrank string + Immutable *DreamerImmutable +} + +type DreamerImmutable struct { + ID string + State *pb.Dreamer_States + MissingCall *pb.Dreamer_MissingCalls + Calls int32 + Started time.Time + Stopped time.Time + LastUpdate time.Time + empty bool +} + +type Slap struct { + Name string + Desc string + DescLong string + Args pb.Message + Tense int32 + Interval int32 + Homeland uint32 + FunnyPrank string + Immutable *SlapImmutable +} + +type SlapImmutable struct { + ID string + Out pb.Message + MildSlap bool + PrettyPrint string + State *pb.Slap_States + Started time.Time + Stopped time.Time + LastUpdate time.Time + LoveRadius *LoveRadius + empty bool +} + +type Goat struct { + Target string + Slaps []Slap + FunnyPrank string + Immutable *GoatImmutable +} + +type GoatImmutable struct { + ID string + State *pb.Goat_States + Started time.Time + Stopped time.Time + LastUpdate time.Time + empty bool +} +type Donkey struct { + Pause bool + Sleep int32 + FunnyPrank string + Immutable *DonkeyImmutable +} + +type DonkeyImmutable struct { + ID string + State *pb.Donkey_States + Started time.Time + Stopped time.Time + LastUpdate time.Time + empty bool +} + +type LoveRadius struct { + Summer *SummerLove + empty bool +} + +type SummerLove struct { + Summary *SummerLoveSummary + empty bool +} + +type SummerLoveSummary struct { + Devices []string + ChangeType []pb.SummerType + empty bool +} + +func (EagleImmutable) Proto() *pb.Eagle { return nil } +func (DreamerImmutable) Proto() *pb.Dreamer { return nil } +func (SlapImmutable) Proto() *pb.Slap { return nil } +func (GoatImmutable) Proto() *pb.Goat { return nil } +func (DonkeyImmutable) Proto() *pb.Donkey { return nil } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project2.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project2.go new file mode 100644 index 0000000..1616dd8 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project2.go @@ -0,0 +1,74 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package teststructs + +import ( + "time" + + pb "github.com/google/go-cmp/cmp/internal/testprotos" +) + +// This is an sanitized example of equality from a real use-case. +// The original equality function was as follows: +/* +func equalBatch(b1, b2 *GermBatch) bool { + for _, b := range []*GermBatch{b1, b2} { + for _, l := range b.DirtyGerms { + sort.Slice(l, func(i, j int) bool { return l[i].String() < l[j].String() }) + } + for _, l := range b.CleanGerms { + sort.Slice(l, func(i, j int) bool { return l[i].String() < l[j].String() }) + } + } + if !pb.DeepEqual(b1.DirtyGerms, b2.DirtyGerms) || + !pb.DeepEqual(b1.CleanGerms, b2.CleanGerms) || + !pb.DeepEqual(b1.GermMap, b2.GermMap) { + return false + } + if len(b1.DishMap) != len(b2.DishMap) { + return false + } + for id := range b1.DishMap { + kpb1, err1 := b1.DishMap[id].Proto() + kpb2, err2 := b2.DishMap[id].Proto() + if !pb.Equal(kpb1, kpb2) || !reflect.DeepEqual(err1, err2) { + return false + } + } + return b1.HasPreviousResult == b2.HasPreviousResult && + b1.DirtyID == b2.DirtyID && + b1.CleanID == b2.CleanID && + b1.GermStrain == b2.GermStrain && + b1.TotalDirtyGerms == b2.TotalDirtyGerms && + b1.InfectedAt.Equal(b2.InfectedAt) +} +*/ + +type GermBatch struct { + DirtyGerms, CleanGerms map[int32][]*pb.Germ + GermMap map[int32]*pb.Germ + DishMap map[int32]*Dish + HasPreviousResult bool + DirtyID, CleanID int32 + GermStrain int32 + TotalDirtyGerms int + InfectedAt time.Time +} + +type Dish struct { + pb *pb.Dish + err error +} + +func CreateDish(m *pb.Dish, err error) *Dish { + return &Dish{pb: m, err: err} +} + +func (d *Dish) Proto() (*pb.Dish, error) { + if d.err != nil { + return nil, d.err + } + return d.pb, nil +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project3.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project3.go new file mode 100644 index 0000000..9e56dfa --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project3.go @@ -0,0 +1,82 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package teststructs + +import ( + "sync" + + pb "github.com/google/go-cmp/cmp/internal/testprotos" +) + +// This is an sanitized example of equality from a real use-case. +// The original equality function was as follows: +/* +func equalDirt(x, y *Dirt) bool { + if !reflect.DeepEqual(x.table, y.table) || + !reflect.DeepEqual(x.ts, y.ts) || + x.Discord != y.Discord || + !pb.Equal(&x.Proto, &y.Proto) || + len(x.wizard) != len(y.wizard) || + len(x.sadistic) != len(y.sadistic) || + x.lastTime != y.lastTime { + return false + } + for k, vx := range x.wizard { + vy, ok := y.wizard[k] + if !ok || !pb.Equal(vx, vy) { + return false + } + } + for k, vx := range x.sadistic { + vy, ok := y.sadistic[k] + if !ok || !pb.Equal(vx, vy) { + return false + } + } + return true +} +*/ + +type FakeMutex struct { + sync.Locker + x struct{} +} + +type Dirt struct { + table Table // Always concrete type of MockTable + ts Timestamp + Discord DiscordState + Proto pb.Dirt + wizard map[string]*pb.Wizard + sadistic map[string]*pb.Sadistic + lastTime int64 + mu FakeMutex +} + +type DiscordState int + +type Timestamp int64 + +func (d *Dirt) SetTable(t Table) { d.table = t } +func (d *Dirt) SetTimestamp(t Timestamp) { d.ts = t } +func (d *Dirt) SetWizard(m map[string]*pb.Wizard) { d.wizard = m } +func (d *Dirt) SetSadistic(m map[string]*pb.Sadistic) { d.sadistic = m } +func (d *Dirt) SetLastTime(t int64) { d.lastTime = t } + +type Table interface { + Operation1() error + Operation2() error + Operation3() error +} + +type MockTable struct { + state []string +} + +func CreateMockTable(s []string) *MockTable { return &MockTable{s} } +func (mt *MockTable) Operation1() error { return nil } +func (mt *MockTable) Operation2() error { return nil } +func (mt *MockTable) Operation3() error { return nil } +func (mt *MockTable) State() []string { return mt.state } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project4.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project4.go new file mode 100644 index 0000000..a09aba2 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/project4.go @@ -0,0 +1,142 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package teststructs + +import ( + "time" + + pb "github.com/google/go-cmp/cmp/internal/testprotos" +) + +// This is an sanitized example of equality from a real use-case. +// The original equality function was as follows: +/* +func equalCartel(x, y Cartel) bool { + if !(equalHeadquarter(x.Headquarter, y.Headquarter) && + x.Source() == y.Source() && + x.CreationDate().Equal(y.CreationDate()) && + x.Boss() == y.Boss() && + x.LastCrimeDate().Equal(y.LastCrimeDate())) { + return false + } + if len(x.Poisons()) != len(y.Poisons()) { + return false + } + for i := range x.Poisons() { + if !equalPoison(*x.Poisons()[i], *y.Poisons()[i]) { + return false + } + } + return true +} +func equalHeadquarter(x, y Headquarter) bool { + xr, yr := x.Restrictions(), y.Restrictions() + return x.ID() == y.ID() && + x.Location() == y.Location() && + reflect.DeepEqual(x.SubDivisions(), y.SubDivisions()) && + x.IncorporatedDate().Equal(y.IncorporatedDate()) && + pb.Equal(x.MetaData(), y.MetaData()) && + bytes.Equal(x.PrivateMessage(), y.PrivateMessage()) && + bytes.Equal(x.PublicMessage(), y.PublicMessage()) && + x.HorseBack() == y.HorseBack() && + x.Rattle() == y.Rattle() && + x.Convulsion() == y.Convulsion() && + x.Expansion() == y.Expansion() && + x.Status() == y.Status() && + pb.Equal(&xr, &yr) && + x.CreationTime().Equal(y.CreationTime()) +} +func equalPoison(x, y Poison) bool { + return x.PoisonType() == y.PoisonType() && + x.Expiration().Equal(y.Expiration()) && + x.Manufacturer() == y.Manufacturer() && + x.Potency() == y.Potency() +} +*/ + +type Cartel struct { + Headquarter + source string + creationDate time.Time + boss string + lastCrimeDate time.Time + poisons []*Poison +} + +func (p Cartel) Source() string { return p.source } +func (p Cartel) CreationDate() time.Time { return p.creationDate } +func (p Cartel) Boss() string { return p.boss } +func (p Cartel) LastCrimeDate() time.Time { return p.lastCrimeDate } +func (p Cartel) Poisons() []*Poison { return p.poisons } + +func (p *Cartel) SetSource(x string) { p.source = x } +func (p *Cartel) SetCreationDate(x time.Time) { p.creationDate = x } +func (p *Cartel) SetBoss(x string) { p.boss = x } +func (p *Cartel) SetLastCrimeDate(x time.Time) { p.lastCrimeDate = x } +func (p *Cartel) SetPoisons(x []*Poison) { p.poisons = x } + +type Headquarter struct { + id uint64 + location string + subDivisions []string + incorporatedDate time.Time + metaData *pb.MetaData + privateMessage []byte + publicMessage []byte + horseBack string + rattle string + convulsion bool + expansion uint64 + status pb.HoneyStatus + restrictions pb.Restrictions + creationTime time.Time +} + +func (hq Headquarter) ID() uint64 { return hq.id } +func (hq Headquarter) Location() string { return hq.location } +func (hq Headquarter) SubDivisions() []string { return hq.subDivisions } +func (hq Headquarter) IncorporatedDate() time.Time { return hq.incorporatedDate } +func (hq Headquarter) MetaData() *pb.MetaData { return hq.metaData } +func (hq Headquarter) PrivateMessage() []byte { return hq.privateMessage } +func (hq Headquarter) PublicMessage() []byte { return hq.publicMessage } +func (hq Headquarter) HorseBack() string { return hq.horseBack } +func (hq Headquarter) Rattle() string { return hq.rattle } +func (hq Headquarter) Convulsion() bool { return hq.convulsion } +func (hq Headquarter) Expansion() uint64 { return hq.expansion } +func (hq Headquarter) Status() pb.HoneyStatus { return hq.status } +func (hq Headquarter) Restrictions() pb.Restrictions { return hq.restrictions } +func (hq Headquarter) CreationTime() time.Time { return hq.creationTime } + +func (hq *Headquarter) SetID(x uint64) { hq.id = x } +func (hq *Headquarter) SetLocation(x string) { hq.location = x } +func (hq *Headquarter) SetSubDivisions(x []string) { hq.subDivisions = x } +func (hq *Headquarter) SetIncorporatedDate(x time.Time) { hq.incorporatedDate = x } +func (hq *Headquarter) SetMetaData(x *pb.MetaData) { hq.metaData = x } +func (hq *Headquarter) SetPrivateMessage(x []byte) { hq.privateMessage = x } +func (hq *Headquarter) SetPublicMessage(x []byte) { hq.publicMessage = x } +func (hq *Headquarter) SetHorseBack(x string) { hq.horseBack = x } +func (hq *Headquarter) SetRattle(x string) { hq.rattle = x } +func (hq *Headquarter) SetConvulsion(x bool) { hq.convulsion = x } +func (hq *Headquarter) SetExpansion(x uint64) { hq.expansion = x } +func (hq *Headquarter) SetStatus(x pb.HoneyStatus) { hq.status = x } +func (hq *Headquarter) SetRestrictions(x pb.Restrictions) { hq.restrictions = x } +func (hq *Headquarter) SetCreationTime(x time.Time) { hq.creationTime = x } + +type Poison struct { + poisonType pb.PoisonType + expiration time.Time + manufacturer string + potency int +} + +func (p Poison) PoisonType() pb.PoisonType { return p.poisonType } +func (p Poison) Expiration() time.Time { return p.expiration } +func (p Poison) Manufacturer() string { return p.manufacturer } +func (p Poison) Potency() int { return p.potency } + +func (p *Poison) SetPoisonType(x pb.PoisonType) { p.poisonType = x } +func (p *Poison) SetExpiration(x time.Time) { p.expiration = x } +func (p *Poison) SetManufacturer(x string) { p.manufacturer = x } +func (p *Poison) SetPotency(x int) { p.potency = x } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/structs.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/structs.go new file mode 100644 index 0000000..bfd2de8 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/teststructs/structs.go @@ -0,0 +1,197 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package teststructs + +type InterfaceA interface { + InterfaceA() +} + +type ( + StructA struct{ X string } // Equal method on value receiver + StructB struct{ X string } // Equal method on pointer receiver + StructC struct{ X string } // Equal method (with interface argument) on value receiver + StructD struct{ X string } // Equal method (with interface argument) on pointer receiver + StructE struct{ X string } // Equal method (with interface argument on value receiver) on pointer receiver + StructF struct{ X string } // Equal method (with interface argument on pointer receiver) on value receiver + + // These embed the above types as a value. + StructA1 struct { + StructA + X string + } + StructB1 struct { + StructB + X string + } + StructC1 struct { + StructC + X string + } + StructD1 struct { + StructD + X string + } + StructE1 struct { + StructE + X string + } + StructF1 struct { + StructF + X string + } + + // These embed the above types as a pointer. + StructA2 struct { + *StructA + X string + } + StructB2 struct { + *StructB + X string + } + StructC2 struct { + *StructC + X string + } + StructD2 struct { + *StructD + X string + } + StructE2 struct { + *StructE + X string + } + StructF2 struct { + *StructF + X string + } + + StructNo struct{ X string } // Equal method (with interface argument) on non-satisfying receiver + + AssignA func() int + AssignB struct{ A int } + AssignC chan bool + AssignD <-chan bool +) + +func (x StructA) Equal(y StructA) bool { return true } +func (x *StructB) Equal(y *StructB) bool { return true } +func (x StructC) Equal(y InterfaceA) bool { return true } +func (x StructC) InterfaceA() {} +func (x *StructD) Equal(y InterfaceA) bool { return true } +func (x *StructD) InterfaceA() {} +func (x *StructE) Equal(y InterfaceA) bool { return true } +func (x StructE) InterfaceA() {} +func (x StructF) Equal(y InterfaceA) bool { return true } +func (x *StructF) InterfaceA() {} +func (x StructNo) Equal(y InterfaceA) bool { return true } + +func (x AssignA) Equal(y func() int) bool { return true } +func (x AssignB) Equal(y struct{ A int }) bool { return true } +func (x AssignC) Equal(y chan bool) bool { return true } +func (x AssignD) Equal(y <-chan bool) bool { return true } + +var _ = func( + a StructA, b StructB, c StructC, d StructD, e StructE, f StructF, + ap *StructA, bp *StructB, cp *StructC, dp *StructD, ep *StructE, fp *StructF, + a1 StructA1, b1 StructB1, c1 StructC1, d1 StructD1, e1 StructE1, f1 StructF1, + a2 StructA2, b2 StructB2, c2 StructC2, d2 StructD2, e2 StructE2, f2 StructF1, +) { + a.Equal(a) + b.Equal(&b) + c.Equal(c) + d.Equal(&d) + e.Equal(e) + f.Equal(&f) + + ap.Equal(*ap) + bp.Equal(bp) + cp.Equal(*cp) + dp.Equal(dp) + ep.Equal(*ep) + fp.Equal(fp) + + a1.Equal(a1.StructA) + b1.Equal(&b1.StructB) + c1.Equal(c1) + d1.Equal(&d1) + e1.Equal(e1) + f1.Equal(&f1) + + a2.Equal(*a2.StructA) + b2.Equal(b2.StructB) + c2.Equal(c2) + d2.Equal(&d2) + e2.Equal(e2) + f2.Equal(&f2) +} + +type ( + privateStruct struct{ Public, private int } + PublicStruct struct{ Public, private int } + ParentStructA struct{ privateStruct } + ParentStructB struct{ PublicStruct } + ParentStructC struct { + privateStruct + Public, private int + } + ParentStructD struct { + PublicStruct + Public, private int + } + ParentStructE struct { + privateStruct + PublicStruct + } + ParentStructF struct { + privateStruct + PublicStruct + Public, private int + } + ParentStructG struct { + *privateStruct + } + ParentStructH struct { + *PublicStruct + } + ParentStructI struct { + *privateStruct + *PublicStruct + } + ParentStructJ struct { + *privateStruct + *PublicStruct + Public PublicStruct + private privateStruct + } +) + +func NewParentStructG() *ParentStructG { + return &ParentStructG{new(privateStruct)} +} +func NewParentStructH() *ParentStructH { + return &ParentStructH{new(PublicStruct)} +} +func NewParentStructI() *ParentStructI { + return &ParentStructI{new(privateStruct), new(PublicStruct)} +} +func NewParentStructJ() *ParentStructJ { + return &ParentStructJ{ + privateStruct: new(privateStruct), PublicStruct: new(PublicStruct), + } +} +func (s *privateStruct) SetPrivate(i int) { s.private = i } +func (s *PublicStruct) SetPrivate(i int) { s.private = i } +func (s *ParentStructC) SetPrivate(i int) { s.private = i } +func (s *ParentStructD) SetPrivate(i int) { s.private = i } +func (s *ParentStructF) SetPrivate(i int) { s.private = i } +func (s *ParentStructA) PrivateStruct() *privateStruct { return &s.privateStruct } +func (s *ParentStructC) PrivateStruct() *privateStruct { return &s.privateStruct } +func (s *ParentStructE) PrivateStruct() *privateStruct { return &s.privateStruct } +func (s *ParentStructF) PrivateStruct() *privateStruct { return &s.privateStruct } +func (s *ParentStructG) PrivateStruct() *privateStruct { return s.privateStruct } +func (s *ParentStructI) PrivateStruct() *privateStruct { return s.privateStruct } +func (s *ParentStructJ) PrivateStruct() *privateStruct { return s.privateStruct } +func (s *ParentStructJ) Private() *privateStruct { return &s.private } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name.go new file mode 100644 index 0000000..b6c12ce --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name.go @@ -0,0 +1,157 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "reflect" + "strconv" +) + +// TypeString is nearly identical to reflect.Type.String, +// but has an additional option to specify that full type names be used. +func TypeString(t reflect.Type, qualified bool) string { + return string(appendTypeName(nil, t, qualified, false)) +} + +func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte { + // BUG: Go reflection provides no way to disambiguate two named types + // of the same name and within the same package, + // but declared within the namespace of different functions. + + // Named type. + if t.Name() != "" { + if qualified && t.PkgPath() != "" { + b = append(b, '"') + b = append(b, t.PkgPath()...) + b = append(b, '"') + b = append(b, '.') + b = append(b, t.Name()...) + } else { + b = append(b, t.String()...) + } + return b + } + + // Unnamed type. + switch k := t.Kind(); k { + case reflect.Bool, reflect.String, reflect.UnsafePointer, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + b = append(b, k.String()...) + case reflect.Chan: + if t.ChanDir() == reflect.RecvDir { + b = append(b, "<-"...) + } + b = append(b, "chan"...) + if t.ChanDir() == reflect.SendDir { + b = append(b, "<-"...) + } + b = append(b, ' ') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Func: + if !elideFunc { + b = append(b, "func"...) + } + b = append(b, '(') + for i := 0; i < t.NumIn(); i++ { + if i > 0 { + b = append(b, ", "...) + } + if i == t.NumIn()-1 && t.IsVariadic() { + b = append(b, "..."...) + b = appendTypeName(b, t.In(i).Elem(), qualified, false) + } else { + b = appendTypeName(b, t.In(i), qualified, false) + } + } + b = append(b, ')') + switch t.NumOut() { + case 0: + // Do nothing + case 1: + b = append(b, ' ') + b = appendTypeName(b, t.Out(0), qualified, false) + default: + b = append(b, " ("...) + for i := 0; i < t.NumOut(); i++ { + if i > 0 { + b = append(b, ", "...) + } + b = appendTypeName(b, t.Out(i), qualified, false) + } + b = append(b, ')') + } + case reflect.Struct: + b = append(b, "struct{ "...) + for i := 0; i < t.NumField(); i++ { + if i > 0 { + b = append(b, "; "...) + } + sf := t.Field(i) + if !sf.Anonymous { + if qualified && sf.PkgPath != "" { + b = append(b, '"') + b = append(b, sf.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, sf.Name...) + b = append(b, ' ') + } + b = appendTypeName(b, sf.Type, qualified, false) + if sf.Tag != "" { + b = append(b, ' ') + b = strconv.AppendQuote(b, string(sf.Tag)) + } + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + case reflect.Slice, reflect.Array: + b = append(b, '[') + if k == reflect.Array { + b = strconv.AppendUint(b, uint64(t.Len()), 10) + } + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Map: + b = append(b, "map["...) + b = appendTypeName(b, t.Key(), qualified, false) + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Ptr: + b = append(b, '*') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Interface: + b = append(b, "interface{ "...) + for i := 0; i < t.NumMethod(); i++ { + if i > 0 { + b = append(b, "; "...) + } + m := t.Method(i) + if qualified && m.PkgPath != "" { + b = append(b, '"') + b = append(b, m.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, m.Name...) + b = appendTypeName(b, m.Type, qualified, true) + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + default: + panic("invalid kind: " + k.String()) + } + return b +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name_test.go new file mode 100644 index 0000000..3eec91c --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/name_test.go @@ -0,0 +1,144 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "reflect" + "strings" + "testing" +) + +type Named struct{} + +var pkgPath = reflect.TypeOf(Named{}).PkgPath() + +func TestTypeString(t *testing.T) { + tests := []struct { + in interface{} + want string + }{{ + in: bool(false), + want: "bool", + }, { + in: int(0), + want: "int", + }, { + in: float64(0), + want: "float64", + }, { + in: string(""), + want: "string", + }, { + in: Named{}, + want: "$PackagePath.Named", + }, { + in: (chan Named)(nil), + want: "chan $PackagePath.Named", + }, { + in: (<-chan Named)(nil), + want: "<-chan $PackagePath.Named", + }, { + in: (chan<- Named)(nil), + want: "chan<- $PackagePath.Named", + }, { + in: (func())(nil), + want: "func()", + }, { + in: (func(Named))(nil), + want: "func($PackagePath.Named)", + }, { + in: (func() Named)(nil), + want: "func() $PackagePath.Named", + }, { + in: (func(int, Named) (int, error))(nil), + want: "func(int, $PackagePath.Named) (int, error)", + }, { + in: (func(...Named))(nil), + want: "func(...$PackagePath.Named)", + }, { + in: struct{}{}, + want: "struct{}", + }, { + in: struct{ Named }{}, + want: "struct{ $PackagePath.Named }", + }, { + in: struct { + Named `tag` + }{}, + want: "struct{ $PackagePath.Named \"tag\" }", + }, { + in: struct{ Named Named }{}, + want: "struct{ Named $PackagePath.Named }", + }, { + in: struct { + Named Named `tag` + }{}, + want: "struct{ Named $PackagePath.Named \"tag\" }", + }, { + in: struct { + Int int + Named Named + }{}, + want: "struct{ Int int; Named $PackagePath.Named }", + }, { + in: struct { + _ int + x Named + }{}, + want: "struct{ $FieldPrefix._ int; $FieldPrefix.x $PackagePath.Named }", + }, { + in: []Named(nil), + want: "[]$PackagePath.Named", + }, { + in: []*Named(nil), + want: "[]*$PackagePath.Named", + }, { + in: [10]Named{}, + want: "[10]$PackagePath.Named", + }, { + in: [10]*Named{}, + want: "[10]*$PackagePath.Named", + }, { + in: map[string]string(nil), + want: "map[string]string", + }, { + in: map[Named]Named(nil), + want: "map[$PackagePath.Named]$PackagePath.Named", + }, { + in: (*Named)(nil), + want: "*$PackagePath.Named", + }, { + in: (*interface{})(nil), + want: "*interface{}", + }, { + in: (*interface{ Read([]byte) (int, error) })(nil), + want: "*interface{ Read([]uint8) (int, error) }", + }, { + in: (*interface { + F1() + F2(Named) + F3() Named + F4(int, Named) (int, error) + F5(...Named) + })(nil), + want: "*interface{ F1(); F2($PackagePath.Named); F3() $PackagePath.Named; F4(int, $PackagePath.Named) (int, error); F5(...$PackagePath.Named) }", + }} + + for _, tt := range tests { + typ := reflect.TypeOf(tt.in) + wantShort := tt.want + wantShort = strings.Replace(wantShort, "$PackagePath", "value", -1) + wantShort = strings.Replace(wantShort, "$FieldPrefix.", "", -1) + if gotShort := TypeString(typ, false); gotShort != wantShort { + t.Errorf("TypeString(%v, false) mismatch:\ngot: %v\nwant: %v", typ, gotShort, wantShort) + } + wantQualified := tt.want + wantQualified = strings.Replace(wantQualified, "$PackagePath", `"`+pkgPath+`"`, -1) + wantQualified = strings.Replace(wantQualified, "$FieldPrefix", `"`+pkgPath+`"`, -1) + if gotQualified := TypeString(typ, true); gotQualified != wantQualified { + t.Errorf("TypeString(%v, true) mismatch:\ngot: %v\nwant: %v", typ, gotQualified, wantQualified) + } + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_purego.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_purego.go new file mode 100644 index 0000000..44f4a5a --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_purego.go @@ -0,0 +1,33 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build purego + +package value + +import "reflect" + +// Pointer is an opaque typed pointer and is guaranteed to be comparable. +type Pointer struct { + p uintptr + t reflect.Type +} + +// PointerOf returns a Pointer from v, which must be a +// reflect.Ptr, reflect.Slice, or reflect.Map. +func PointerOf(v reflect.Value) Pointer { + // NOTE: Storing a pointer as an uintptr is technically incorrect as it + // assumes that the GC implementation does not use a moving collector. + return Pointer{v.Pointer(), v.Type()} +} + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == 0 +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return p.p +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_unsafe.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_unsafe.go new file mode 100644 index 0000000..a605953 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/pointer_unsafe.go @@ -0,0 +1,36 @@ +// Copyright 2018, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !purego + +package value + +import ( + "reflect" + "unsafe" +) + +// Pointer is an opaque typed pointer and is guaranteed to be comparable. +type Pointer struct { + p unsafe.Pointer + t reflect.Type +} + +// PointerOf returns a Pointer from v, which must be a +// reflect.Ptr, reflect.Slice, or reflect.Map. +func PointerOf(v reflect.Value) Pointer { + // The proper representation of a pointer is unsafe.Pointer, + // which is necessary if the GC ever uses a moving collector. + return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} +} + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == nil +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return uintptr(p.p) +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort.go new file mode 100644 index 0000000..98533b0 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort.go @@ -0,0 +1,106 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "fmt" + "math" + "reflect" + "sort" +) + +// SortKeys sorts a list of map keys, deduplicating keys if necessary. +// The type of each value must be comparable. +func SortKeys(vs []reflect.Value) []reflect.Value { + if len(vs) == 0 { + return vs + } + + // Sort the map keys. + sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) + + // Deduplicate keys (fails for NaNs). + vs2 := vs[:1] + for _, v := range vs[1:] { + if isLess(vs2[len(vs2)-1], v) { + vs2 = append(vs2, v) + } + } + return vs2 +} + +// isLess is a generic function for sorting arbitrary map keys. +// The inputs must be of the same type and must be comparable. +func isLess(x, y reflect.Value) bool { + switch x.Type().Kind() { + case reflect.Bool: + return !x.Bool() && y.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return x.Int() < y.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return x.Uint() < y.Uint() + case reflect.Float32, reflect.Float64: + // NOTE: This does not sort -0 as less than +0 + // since Go maps treat -0 and +0 as equal keys. + fx, fy := x.Float(), y.Float() + return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) + case reflect.Complex64, reflect.Complex128: + cx, cy := x.Complex(), y.Complex() + rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) + if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { + return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) + } + return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) + case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: + return x.Pointer() < y.Pointer() + case reflect.String: + return x.String() < y.String() + case reflect.Array: + for i := 0; i < x.Len(); i++ { + if isLess(x.Index(i), y.Index(i)) { + return true + } + if isLess(y.Index(i), x.Index(i)) { + return false + } + } + return false + case reflect.Struct: + for i := 0; i < x.NumField(); i++ { + if isLess(x.Field(i), y.Field(i)) { + return true + } + if isLess(y.Field(i), x.Field(i)) { + return false + } + } + return false + case reflect.Interface: + vx, vy := x.Elem(), y.Elem() + if !vx.IsValid() || !vy.IsValid() { + return !vx.IsValid() && vy.IsValid() + } + tx, ty := vx.Type(), vy.Type() + if tx == ty { + return isLess(x.Elem(), y.Elem()) + } + if tx.Kind() != ty.Kind() { + return vx.Kind() < vy.Kind() + } + if tx.String() != ty.String() { + return tx.String() < ty.String() + } + if tx.PkgPath() != ty.PkgPath() { + return tx.PkgPath() < ty.PkgPath() + } + // This can happen in rare situations, so we fallback to just comparing + // the unique pointer for a reflect.Type. This guarantees deterministic + // ordering within a program, but it is obviously not stable. + return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() + default: + // Must be Func, Map, or Slice; which are not comparable. + panic(fmt.Sprintf("%T is not comparable", x.Type())) + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort_test.go new file mode 100644 index 0000000..26222d6 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/sort_test.go @@ -0,0 +1,159 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value_test + +import ( + "math" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/internal/value" +) + +func TestSortKeys(t *testing.T) { + type ( + MyString string + MyArray [2]int + MyStruct struct { + A MyString + B MyArray + C chan float64 + } + EmptyStruct struct{} + ) + + opts := []cmp.Option{ + cmp.Comparer(func(x, y float64) bool { + if math.IsNaN(x) && math.IsNaN(y) { + return true + } + return x == y + }), + cmp.Comparer(func(x, y complex128) bool { + rx, ix, ry, iy := real(x), imag(x), real(y), imag(y) + if math.IsNaN(rx) && math.IsNaN(ry) { + rx, ry = 0, 0 + } + if math.IsNaN(ix) && math.IsNaN(iy) { + ix, iy = 0, 0 + } + return rx == ry && ix == iy + }), + cmp.Comparer(func(x, y chan bool) bool { return true }), + cmp.Comparer(func(x, y chan int) bool { return true }), + cmp.Comparer(func(x, y chan float64) bool { return true }), + cmp.Comparer(func(x, y chan interface{}) bool { return true }), + cmp.Comparer(func(x, y *int) bool { return true }), + } + + tests := []struct { + in map[interface{}]bool // Set of keys to sort + want []interface{} + }{{ + in: map[interface{}]bool{1: true, 2: true, 3: true}, + want: []interface{}{1, 2, 3}, + }, { + in: map[interface{}]bool{ + nil: true, + true: true, + false: true, + -5: true, + -55: true, + -555: true, + uint(1): true, + uint(11): true, + uint(111): true, + "abc": true, + "abcd": true, + "abcde": true, + "foo": true, + "bar": true, + MyString("abc"): true, + MyString("abcd"): true, + MyString("abcde"): true, + new(int): true, + new(int): true, + make(chan bool): true, + make(chan bool): true, + make(chan int): true, + make(chan interface{}): true, + math.Inf(+1): true, + math.Inf(-1): true, + 1.2345: true, + 12.345: true, + 123.45: true, + 1234.5: true, + 0 + 0i: true, + 1 + 0i: true, + 2 + 0i: true, + 0 + 1i: true, + 0 + 2i: true, + 0 + 3i: true, + [2]int{2, 3}: true, + [2]int{4, 0}: true, + [2]int{2, 4}: true, + MyArray([2]int{2, 4}): true, + EmptyStruct{}: true, + MyStruct{ + "bravo", [2]int{2, 3}, make(chan float64), + }: true, + MyStruct{ + "alpha", [2]int{3, 3}, make(chan float64), + }: true, + }, + want: []interface{}{ + nil, false, true, + -555, -55, -5, uint(1), uint(11), uint(111), + math.Inf(-1), 1.2345, 12.345, 123.45, 1234.5, math.Inf(+1), + (0 + 0i), (0 + 1i), (0 + 2i), (0 + 3i), (1 + 0i), (2 + 0i), + [2]int{2, 3}, [2]int{2, 4}, [2]int{4, 0}, MyArray([2]int{2, 4}), + make(chan bool), make(chan bool), make(chan int), make(chan interface{}), + new(int), new(int), + "abc", "abcd", "abcde", "bar", "foo", + MyString("abc"), MyString("abcd"), MyString("abcde"), + EmptyStruct{}, + MyStruct{"alpha", [2]int{3, 3}, make(chan float64)}, + MyStruct{"bravo", [2]int{2, 3}, make(chan float64)}, + }, + }, { + // NaN values cannot be properly deduplicated. + // This is okay since map entries with NaN in the keys cannot be + // retrieved anyways. + in: map[interface{}]bool{ + math.NaN(): true, + math.NaN(): true, + complex(0, math.NaN()): true, + complex(0, math.NaN()): true, + complex(math.NaN(), 0): true, + complex(math.NaN(), 0): true, + complex(math.NaN(), math.NaN()): true, + }, + want: []interface{}{ + math.NaN(), + complex(math.NaN(), math.NaN()), + complex(math.NaN(), 0), + complex(0, math.NaN()), + }, + }} + + for i, tt := range tests { + // Intentionally pass the map via an unexported field to detect panics. + // Unfortunately, we cannot actually test the keys without using unsafe. + v := reflect.ValueOf(struct{ x map[interface{}]bool }{tt.in}).Field(0) + value.SortKeys(append(v.MapKeys(), v.MapKeys()...)) + + // Try again, with keys that have read-write access in reflect. + v = reflect.ValueOf(tt.in) + keys := append(v.MapKeys(), v.MapKeys()...) + var got []interface{} + for _, k := range value.SortKeys(keys) { + got = append(got, k.Interface()) + } + if d := cmp.Diff(got, tt.want, opts...); d != "" { + t.Errorf("test %d, Sort() mismatch (-got +want):\n%s", i, d) + } + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero.go new file mode 100644 index 0000000..9147a29 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero.go @@ -0,0 +1,48 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "math" + "reflect" +) + +// IsZero reports whether v is the zero value. +// This does not rely on Interface and so can be used on unexported fields. +func IsZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return v.Bool() == false + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return math.Float64bits(v.Float()) == 0 + case reflect.Complex64, reflect.Complex128: + return math.Float64bits(real(v.Complex())) == 0 && math.Float64bits(imag(v.Complex())) == 0 + case reflect.String: + return v.String() == "" + case reflect.UnsafePointer: + return v.Pointer() == 0 + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + return v.IsNil() + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if !IsZero(v.Index(i)) { + return false + } + } + return true + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if !IsZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero_test.go new file mode 100644 index 0000000..ddaa337 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/internal/value/zero_test.go @@ -0,0 +1,52 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "archive/tar" + "math" + "reflect" + "testing" +) + +func TestIsZero(t *testing.T) { + tests := []struct { + in interface{} + want bool + }{ + {0, true}, + {1, false}, + {"", true}, + {"foo", false}, + {[]byte(nil), true}, + {[]byte{}, false}, + {map[string]bool(nil), true}, + {map[string]bool{}, false}, + {tar.Header{}, true}, + {&tar.Header{}, false}, + {tar.Header{Name: "foo"}, false}, + {(chan bool)(nil), true}, + {make(chan bool), false}, + {(func(*testing.T))(nil), true}, + {TestIsZero, false}, + {[...]int{0, 0, 0}, true}, + {[...]int{0, 1, 0}, false}, + {math.Copysign(0, +1), true}, + {math.Copysign(0, -1), false}, + {complex(math.Copysign(0, +1), math.Copysign(0, +1)), true}, + {complex(math.Copysign(0, -1), math.Copysign(0, +1)), false}, + {complex(math.Copysign(0, +1), math.Copysign(0, -1)), false}, + {complex(math.Copysign(0, -1), math.Copysign(0, -1)), false}, + } + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + got := IsZero(reflect.ValueOf(tt.in)) + if got != tt.want { + t.Errorf("IsZero(%v) = %v, want %v", tt.in, got, tt.want) + } + }) + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options.go new file mode 100644 index 0000000..e57b9eb --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options.go @@ -0,0 +1,552 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/google/go-cmp/cmp/internal/function" +) + +// Option configures for specific behavior of Equal and Diff. In particular, +// the fundamental Option functions (Ignore, Transformer, and Comparer), +// configure how equality is determined. +// +// The fundamental options may be composed with filters (FilterPath and +// FilterValues) to control the scope over which they are applied. +// +// The cmp/cmpopts package provides helper functions for creating options that +// may be used with Equal and Diff. +type Option interface { + // filter applies all filters and returns the option that remains. + // Each option may only read s.curPath and call s.callTTBFunc. + // + // An Options is returned only if multiple comparers or transformers + // can apply simultaneously and will only contain values of those types + // or sub-Options containing values of those types. + filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption +} + +// applicableOption represents the following types: +// Fundamental: ignore | validator | *comparer | *transformer +// Grouping: Options +type applicableOption interface { + Option + + // apply executes the option, which may mutate s or panic. + apply(s *state, vx, vy reflect.Value) +} + +// coreOption represents the following types: +// Fundamental: ignore | validator | *comparer | *transformer +// Filters: *pathFilter | *valuesFilter +type coreOption interface { + Option + isCore() +} + +type core struct{} + +func (core) isCore() {} + +// Options is a list of Option values that also satisfies the Option interface. +// Helper comparison packages may return an Options value when packing multiple +// Option values into a single Option. When this package processes an Options, +// it will be implicitly expanded into a flat list. +// +// Applying a filter on an Options is equivalent to applying that same filter +// on all individual options held within. +type Options []Option + +func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) { + for _, opt := range opts { + switch opt := opt.filter(s, t, vx, vy); opt.(type) { + case ignore: + return ignore{} // Only ignore can short-circuit evaluation + case validator: + out = validator{} // Takes precedence over comparer or transformer + case *comparer, *transformer, Options: + switch out.(type) { + case nil: + out = opt + case validator: + // Keep validator + case *comparer, *transformer, Options: + out = Options{out, opt} // Conflicting comparers or transformers + } + } + } + return out +} + +func (opts Options) apply(s *state, _, _ reflect.Value) { + const warning = "ambiguous set of applicable options" + const help = "consider using filters to ensure at most one Comparer or Transformer may apply" + var ss []string + for _, opt := range flattenOptions(nil, opts) { + ss = append(ss, fmt.Sprint(opt)) + } + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help)) +} + +func (opts Options) String() string { + var ss []string + for _, opt := range opts { + ss = append(ss, fmt.Sprint(opt)) + } + return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) +} + +// FilterPath returns a new Option where opt is only evaluated if filter f +// returns true for the current Path in the value tree. +// +// This filter is called even if a slice element or map entry is missing and +// provides an opportunity to ignore such cases. The filter function must be +// symmetric such that the filter result is identical regardless of whether the +// missing value is from x or y. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterPath(f func(Path) bool, opt Option) Option { + if f == nil { + panic("invalid path filter function") + } + if opt := normalizeOption(opt); opt != nil { + return &pathFilter{fnc: f, opt: opt} + } + return nil +} + +type pathFilter struct { + core + fnc func(Path) bool + opt Option +} + +func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { + if f.fnc(s.curPath) { + return f.opt.filter(s, t, vx, vy) + } + return nil +} + +func (f pathFilter) String() string { + return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) +} + +// FilterValues returns a new Option where opt is only evaluated if filter f, +// which is a function of the form "func(T, T) bool", returns true for the +// current pair of values being compared. If either value is invalid or +// the type of the values is not assignable to T, then this filter implicitly +// returns false. +// +// The filter function must be +// symmetric (i.e., agnostic to the order of the inputs) and +// deterministic (i.e., produces the same result when given the same inputs). +// If T is an interface, it is possible that f is called with two values with +// different concrete types that both implement T. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterValues(f interface{}, opt Option) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { + panic(fmt.Sprintf("invalid values filter function: %T", f)) + } + if opt := normalizeOption(opt); opt != nil { + vf := &valuesFilter{fnc: v, opt: opt} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + vf.typ = ti + } + return vf + } + return nil +} + +type valuesFilter struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool + opt Option +} + +func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { + if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() { + return nil + } + if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { + return f.opt.filter(s, t, vx, vy) + } + return nil +} + +func (f valuesFilter) String() string { + return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) +} + +// Ignore is an Option that causes all comparisons to be ignored. +// This value is intended to be combined with FilterPath or FilterValues. +// It is an error to pass an unfiltered Ignore option to Equal. +func Ignore() Option { return ignore{} } + +type ignore struct{ core } + +func (ignore) isFiltered() bool { return false } +func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} } +func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) } +func (ignore) String() string { return "Ignore()" } + +// validator is a sentinel Option type to indicate that some options could not +// be evaluated due to unexported fields, missing slice elements, or +// missing map entries. Both values are validator only for unexported fields. +type validator struct{ core } + +func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption { + if !vx.IsValid() || !vy.IsValid() { + return validator{} + } + if !vx.CanInterface() || !vy.CanInterface() { + return validator{} + } + return nil +} +func (validator) apply(s *state, vx, vy reflect.Value) { + // Implies missing slice element or map entry. + if !vx.IsValid() || !vy.IsValid() { + s.report(vx.IsValid() == vy.IsValid(), 0) + return + } + + // Unable to Interface implies unexported field without visibility access. + if !vx.CanInterface() || !vy.CanInterface() { + help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" + var name string + if t := s.curPath.Index(-2).Type(); t.Name() != "" { + // Named type with unexported fields. + name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType + if _, ok := reflect.New(t).Interface().(error); ok { + help = "consider using cmpopts.EquateErrors to compare error values" + } + } else { + // Unnamed type with unexported fields. Derive PkgPath from field. + var pkgPath string + for i := 0; i < t.NumField() && pkgPath == ""; i++ { + pkgPath = t.Field(i).PkgPath + } + name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) + } + panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) + } + + panic("not reachable") +} + +// identRx represents a valid identifier according to the Go specification. +const identRx = `[_\p{L}][_\p{L}\p{N}]*` + +var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) + +// Transformer returns an Option that applies a transformation function that +// converts values of a certain type into that of another. +// +// The transformer f must be a function "func(T) R" that converts values of +// type T to those of type R and is implicitly filtered to input values +// assignable to T. The transformer must not mutate T in any way. +// +// To help prevent some cases of infinite recursive cycles applying the +// same transform to the output of itself (e.g., in the case where the +// input and output types are the same), an implicit filter is added such that +// a transformer is applicable only if that exact transformer is not already +// in the tail of the Path since the last non-Transform step. +// For situations where the implicit filter is still insufficient, +// consider using cmpopts.AcyclicTransformer, which adds a filter +// to prevent the transformer from being recursively applied upon itself. +// +// The name is a user provided label that is used as the Transform.Name in the +// transformation PathStep (and eventually shown in the Diff output). +// The name must be a valid identifier or qualified identifier in Go syntax. +// If empty, an arbitrary name is used. +func Transformer(name string, f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { + panic(fmt.Sprintf("invalid transformer function: %T", f)) + } + if name == "" { + name = function.NameOf(v) + if !identsRx.MatchString(name) { + name = "λ" // Lambda-symbol as placeholder name + } + } else if !identsRx.MatchString(name) { + panic(fmt.Sprintf("invalid name: %q", name)) + } + tr := &transformer{name: name, fnc: reflect.ValueOf(f)} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + tr.typ = ti + } + return tr +} + +type transformer struct { + core + name string + typ reflect.Type // T + fnc reflect.Value // func(T) R +} + +func (tr *transformer) isFiltered() bool { return tr.typ != nil } + +func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption { + for i := len(s.curPath) - 1; i >= 0; i-- { + if t, ok := s.curPath[i].(Transform); !ok { + break // Hit most recent non-Transform step + } else if tr == t.trans { + return nil // Cannot directly use same Transform + } + } + if tr.typ == nil || t.AssignableTo(tr.typ) { + return tr + } + return nil +} + +func (tr *transformer) apply(s *state, vx, vy reflect.Value) { + step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}} + vvx := s.callTRFunc(tr.fnc, vx, step) + vvy := s.callTRFunc(tr.fnc, vy, step) + step.vx, step.vy = vvx, vvy + s.compareAny(step) +} + +func (tr transformer) String() string { + return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) +} + +// Comparer returns an Option that determines whether two values are equal +// to each other. +// +// The comparer f must be a function "func(T, T) bool" and is implicitly +// filtered to input values assignable to T. If T is an interface, it is +// possible that f is called with two values of different concrete types that +// both implement T. +// +// The equality function must be: +// • Symmetric: equal(x, y) == equal(y, x) +// • Deterministic: equal(x, y) == equal(x, y) +// • Pure: equal(x, y) does not modify x or y +func Comparer(f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Equal) || v.IsNil() { + panic(fmt.Sprintf("invalid comparer function: %T", f)) + } + cm := &comparer{fnc: v} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + cm.typ = ti + } + return cm +} + +type comparer struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (cm *comparer) isFiltered() bool { return cm.typ != nil } + +func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption { + if cm.typ == nil || t.AssignableTo(cm.typ) { + return cm + } + return nil +} + +func (cm *comparer) apply(s *state, vx, vy reflect.Value) { + eq := s.callTTBFunc(cm.fnc, vx, vy) + s.report(eq, reportByFunc) +} + +func (cm comparer) String() string { + return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) +} + +// Exporter returns an Option that specifies whether Equal is allowed to +// introspect into the unexported fields of certain struct types. +// +// Users of this option must understand that comparing on unexported fields +// from external packages is not safe since changes in the internal +// implementation of some external package may cause the result of Equal +// to unexpectedly change. However, it may be valid to use this option on types +// defined in an internal package where the semantic meaning of an unexported +// field is in the control of the user. +// +// In many cases, a custom Comparer should be used instead that defines +// equality as a function of the public API of a type rather than the underlying +// unexported implementation. +// +// For example, the reflect.Type documentation defines equality to be determined +// by the == operator on the interface (essentially performing a shallow pointer +// comparison) and most attempts to compare *regexp.Regexp types are interested +// in only checking that the regular expression strings are equal. +// Both of these are accomplished using Comparers: +// +// Comparer(func(x, y reflect.Type) bool { return x == y }) +// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) +// +// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore +// all unexported fields on specified struct types. +func Exporter(f func(reflect.Type) bool) Option { + if !supportExporters { + panic("Exporter is not supported on purego builds") + } + return exporter(f) +} + +type exporter func(reflect.Type) bool + +func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// AllowUnexported returns an Options that allows Equal to forcibly introspect +// unexported fields of the specified struct types. +// +// See Exporter for the proper use of this option. +func AllowUnexported(types ...interface{}) Option { + m := make(map[reflect.Type]bool) + for _, typ := range types { + t := reflect.TypeOf(typ) + if t.Kind() != reflect.Struct { + panic(fmt.Sprintf("invalid struct type: %T", typ)) + } + m[t] = true + } + return exporter(func(t reflect.Type) bool { return m[t] }) +} + +// Result represents the comparison result for a single node and +// is provided by cmp when calling Result (see Reporter). +type Result struct { + _ [0]func() // Make Result incomparable + flags resultFlags +} + +// Equal reports whether the node was determined to be equal or not. +// As a special case, ignored nodes are considered equal. +func (r Result) Equal() bool { + return r.flags&(reportEqual|reportByIgnore) != 0 +} + +// ByIgnore reports whether the node is equal because it was ignored. +// This never reports true if Equal reports false. +func (r Result) ByIgnore() bool { + return r.flags&reportByIgnore != 0 +} + +// ByMethod reports whether the Equal method determined equality. +func (r Result) ByMethod() bool { + return r.flags&reportByMethod != 0 +} + +// ByFunc reports whether a Comparer function determined equality. +func (r Result) ByFunc() bool { + return r.flags&reportByFunc != 0 +} + +// ByCycle reports whether a reference cycle was detected. +func (r Result) ByCycle() bool { + return r.flags&reportByCycle != 0 +} + +type resultFlags uint + +const ( + _ resultFlags = (1 << iota) / 2 + + reportEqual + reportUnequal + reportByIgnore + reportByMethod + reportByFunc + reportByCycle +) + +// Reporter is an Option that can be passed to Equal. When Equal traverses +// the value trees, it calls PushStep as it descends into each node in the +// tree and PopStep as it ascend out of the node. The leaves of the tree are +// either compared (determined to be equal or not equal) or ignored and reported +// as such by calling the Report method. +func Reporter(r interface { + // PushStep is called when a tree-traversal operation is performed. + // The PathStep itself is only valid until the step is popped. + // The PathStep.Values are valid for the duration of the entire traversal + // and must not be mutated. + // + // Equal always calls PushStep at the start to provide an operation-less + // PathStep used to report the root values. + // + // Within a slice, the exact set of inserted, removed, or modified elements + // is unspecified and may change in future implementations. + // The entries of a map are iterated through in an unspecified order. + PushStep(PathStep) + + // Report is called exactly once on leaf nodes to report whether the + // comparison identified the node as equal, unequal, or ignored. + // A leaf node is one that is immediately preceded by and followed by + // a pair of PushStep and PopStep calls. + Report(Result) + + // PopStep ascends back up the value tree. + // There is always a matching pop call for every push call. + PopStep() +}) Option { + return reporter{r} +} + +type reporter struct{ reporterIface } +type reporterIface interface { + PushStep(PathStep) + Report(Result) + PopStep() +} + +func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// normalizeOption normalizes the input options such that all Options groups +// are flattened and groups with a single element are reduced to that element. +// Only coreOptions and Options containing coreOptions are allowed. +func normalizeOption(src Option) Option { + switch opts := flattenOptions(nil, Options{src}); len(opts) { + case 0: + return nil + case 1: + return opts[0] + default: + return opts + } +} + +// flattenOptions copies all options in src to dst as a flat list. +// Only coreOptions and Options containing coreOptions are allowed. +func flattenOptions(dst, src Options) Options { + for _, opt := range src { + switch opt := opt.(type) { + case nil: + continue + case Options: + dst = flattenOptions(dst, opt) + case coreOption: + dst = append(dst, opt) + default: + panic(fmt.Sprintf("invalid option type: %T", opt)) + } + } + return dst +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options_test.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options_test.go new file mode 100644 index 0000000..c7d45f3 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/options_test.go @@ -0,0 +1,216 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "io" + "reflect" + "strings" + "testing" + + ts "github.com/google/go-cmp/cmp/internal/teststructs" +) + +// Test that the creation of Option values with non-sensible inputs produces +// a run-time panic with a decent error message +func TestOptionPanic(t *testing.T) { + type myBool bool + tests := []struct { + label string // Test description + fnc interface{} // Option function to call + args []interface{} // Arguments to pass in + wantPanic string // Expected panic message + }{{ + label: "AllowUnexported", + fnc: AllowUnexported, + args: []interface{}{}, + }, { + label: "AllowUnexported", + fnc: AllowUnexported, + args: []interface{}{1}, + wantPanic: "invalid struct type", + }, { + label: "AllowUnexported", + fnc: AllowUnexported, + args: []interface{}{ts.StructA{}}, + }, { + label: "AllowUnexported", + fnc: AllowUnexported, + args: []interface{}{ts.StructA{}, ts.StructB{}, ts.StructA{}}, + }, { + label: "AllowUnexported", + fnc: AllowUnexported, + args: []interface{}{ts.StructA{}, &ts.StructB{}, ts.StructA{}}, + wantPanic: "invalid struct type", + }, { + label: "Comparer", + fnc: Comparer, + args: []interface{}{5}, + wantPanic: "invalid comparer function", + }, { + label: "Comparer", + fnc: Comparer, + args: []interface{}{func(x, y interface{}) bool { return true }}, + }, { + label: "Comparer", + fnc: Comparer, + args: []interface{}{func(x, y io.Reader) bool { return true }}, + }, { + label: "Comparer", + fnc: Comparer, + args: []interface{}{func(x, y io.Reader) myBool { return true }}, + wantPanic: "invalid comparer function", + }, { + label: "Comparer", + fnc: Comparer, + args: []interface{}{func(x string, y interface{}) bool { return true }}, + wantPanic: "invalid comparer function", + }, { + label: "Comparer", + fnc: Comparer, + args: []interface{}{(func(int, int) bool)(nil)}, + wantPanic: "invalid comparer function", + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"", 0}, + wantPanic: "invalid transformer function", + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"", func(int) int { return 0 }}, + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"", func(bool) bool { return true }}, + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"", func(int) bool { return true }}, + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"", func(int, int) bool { return true }}, + wantPanic: "invalid transformer function", + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"", (func(int) uint)(nil)}, + wantPanic: "invalid transformer function", + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"Func", func(Path) Path { return nil }}, + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"世界", func(int) bool { return true }}, + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"/*", func(int) bool { return true }}, + wantPanic: "invalid name", + }, { + label: "Transformer", + fnc: Transformer, + args: []interface{}{"_", func(int) bool { return true }}, + }, { + label: "FilterPath", + fnc: FilterPath, + args: []interface{}{(func(Path) bool)(nil), Ignore()}, + wantPanic: "invalid path filter function", + }, { + label: "FilterPath", + fnc: FilterPath, + args: []interface{}{func(Path) bool { return true }, Ignore()}, + }, { + label: "FilterPath", + fnc: FilterPath, + args: []interface{}{func(Path) bool { return true }, Reporter(&defaultReporter{})}, + wantPanic: "invalid option type", + }, { + label: "FilterPath", + fnc: FilterPath, + args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Ignore()}}, + }, { + label: "FilterPath", + fnc: FilterPath, + args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, + wantPanic: "invalid option type", + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{0, Ignore()}, + wantPanic: "invalid values filter function", + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(x, y int) bool { return true }, Ignore()}, + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(x, y interface{}) bool { return true }, Ignore()}, + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(x, y interface{}) myBool { return true }, Ignore()}, + wantPanic: "invalid values filter function", + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(x io.Reader, y interface{}) bool { return true }, Ignore()}, + wantPanic: "invalid values filter function", + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{(func(int, int) bool)(nil), Ignore()}, + wantPanic: "invalid values filter function", + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(int, int) bool { return true }, Reporter(&defaultReporter{})}, + wantPanic: "invalid option type", + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Ignore()}}, + }, { + label: "FilterValues", + fnc: FilterValues, + args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, + wantPanic: "invalid option type", + }} + + for _, tt := range tests { + t.Run(tt.label, func(t *testing.T) { + var gotPanic string + func() { + defer func() { + if ex := recover(); ex != nil { + if s, ok := ex.(string); ok { + gotPanic = s + } else { + panic(ex) + } + } + }() + var vargs []reflect.Value + for _, arg := range tt.args { + vargs = append(vargs, reflect.ValueOf(arg)) + } + reflect.ValueOf(tt.fnc).Call(vargs) + }() + if tt.wantPanic == "" { + if gotPanic != "" { + t.Fatalf("unexpected panic message: %s", gotPanic) + } + } else { + if !strings.Contains(gotPanic, tt.wantPanic) { + t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic) + } + } + }) + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/path.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/path.go new file mode 100644 index 0000000..3d45c1a --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/path.go @@ -0,0 +1,378 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" +) + +// Path is a list of PathSteps describing the sequence of operations to get +// from some root type to the current position in the value tree. +// The first Path element is always an operation-less PathStep that exists +// simply to identify the initial type. +// +// When traversing structs with embedded structs, the embedded struct will +// always be accessed as a field before traversing the fields of the +// embedded struct themselves. That is, an exported field from the +// embedded struct will never be accessed directly from the parent struct. +type Path []PathStep + +// PathStep is a union-type for specific operations to traverse +// a value's tree structure. Users of this package never need to implement +// these types as values of this type will be returned by this package. +// +// Implementations of this interface are +// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. +type PathStep interface { + String() string + + // Type is the resulting type after performing the path step. + Type() reflect.Type + + // Values is the resulting values after performing the path step. + // The type of each valid value is guaranteed to be identical to Type. + // + // In some cases, one or both may be invalid or have restrictions: + // • For StructField, both are not interface-able if the current field + // is unexported and the struct type is not explicitly permitted by + // an Exporter to traverse unexported fields. + // • For SliceIndex, one may be invalid if an element is missing from + // either the x or y slice. + // • For MapIndex, one may be invalid if an entry is missing from + // either the x or y map. + // + // The provided values must not be mutated. + Values() (vx, vy reflect.Value) +} + +var ( + _ PathStep = StructField{} + _ PathStep = SliceIndex{} + _ PathStep = MapIndex{} + _ PathStep = Indirect{} + _ PathStep = TypeAssertion{} + _ PathStep = Transform{} +) + +func (pa *Path) push(s PathStep) { + *pa = append(*pa, s) +} + +func (pa *Path) pop() { + *pa = (*pa)[:len(*pa)-1] +} + +// Last returns the last PathStep in the Path. +// If the path is empty, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Last() PathStep { + return pa.Index(-1) +} + +// Index returns the ith step in the Path and supports negative indexing. +// A negative index starts counting from the tail of the Path such that -1 +// refers to the last step, -2 refers to the second-to-last step, and so on. +// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Index(i int) PathStep { + if i < 0 { + i = len(pa) + i + } + if i < 0 || i >= len(pa) { + return pathStep{} + } + return pa[i] +} + +// String returns the simplified path to a node. +// The simplified path only contains struct field accesses. +// +// For example: +// MyMap.MySlices.MyField +func (pa Path) String() string { + var ss []string + for _, s := range pa { + if _, ok := s.(StructField); ok { + ss = append(ss, s.String()) + } + } + return strings.TrimPrefix(strings.Join(ss, ""), ".") +} + +// GoString returns the path to a specific node using Go syntax. +// +// For example: +// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField +func (pa Path) GoString() string { + var ssPre, ssPost []string + var numIndirect int + for i, s := range pa { + var nextStep PathStep + if i+1 < len(pa) { + nextStep = pa[i+1] + } + switch s := s.(type) { + case Indirect: + numIndirect++ + pPre, pPost := "(", ")" + switch nextStep.(type) { + case Indirect: + continue // Next step is indirection, so let them batch up + case StructField: + numIndirect-- // Automatic indirection on struct fields + case nil: + pPre, pPost = "", "" // Last step; no need for parenthesis + } + if numIndirect > 0 { + ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) + ssPost = append(ssPost, pPost) + } + numIndirect = 0 + continue + case Transform: + ssPre = append(ssPre, s.trans.name+"(") + ssPost = append(ssPost, ")") + continue + } + ssPost = append(ssPost, s.String()) + } + for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { + ssPre[i], ssPre[j] = ssPre[j], ssPre[i] + } + return strings.Join(ssPre, "") + strings.Join(ssPost, "") +} + +type pathStep struct { + typ reflect.Type + vx, vy reflect.Value +} + +func (ps pathStep) Type() reflect.Type { return ps.typ } +func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy } +func (ps pathStep) String() string { + if ps.typ == nil { + return "" + } + s := ps.typ.String() + if s == "" || strings.ContainsAny(s, "{}\n") { + return "root" // Type too simple or complex to print + } + return fmt.Sprintf("{%s}", s) +} + +// StructField represents a struct field access on a field called Name. +type StructField struct{ *structField } +type structField struct { + pathStep + name string + idx int + + // These fields are used for forcibly accessing an unexported field. + // pvx, pvy, and field are only valid if unexported is true. + unexported bool + mayForce bool // Forcibly allow visibility + paddr bool // Was parent addressable? + pvx, pvy reflect.Value // Parent values (always addressible) + field reflect.StructField // Field information +} + +func (sf StructField) Type() reflect.Type { return sf.typ } +func (sf StructField) Values() (vx, vy reflect.Value) { + if !sf.unexported { + return sf.vx, sf.vy // CanInterface reports true + } + + // Forcibly obtain read-write access to an unexported struct field. + if sf.mayForce { + vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr) + vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr) + return vx, vy // CanInterface reports true + } + return sf.vx, sf.vy // CanInterface reports false +} +func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } + +// Name is the field name. +func (sf StructField) Name() string { return sf.name } + +// Index is the index of the field in the parent struct type. +// See reflect.Type.Field. +func (sf StructField) Index() int { return sf.idx } + +// SliceIndex is an index operation on a slice or array at some index Key. +type SliceIndex struct{ *sliceIndex } +type sliceIndex struct { + pathStep + xkey, ykey int + isSlice bool // False for reflect.Array +} + +func (si SliceIndex) Type() reflect.Type { return si.typ } +func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy } +func (si SliceIndex) String() string { + switch { + case si.xkey == si.ykey: + return fmt.Sprintf("[%d]", si.xkey) + case si.ykey == -1: + // [5->?] means "I don't know where X[5] went" + return fmt.Sprintf("[%d->?]", si.xkey) + case si.xkey == -1: + // [?->3] means "I don't know where Y[3] came from" + return fmt.Sprintf("[?->%d]", si.ykey) + default: + // [5->3] means "X[5] moved to Y[3]" + return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) + } +} + +// Key is the index key; it may return -1 if in a split state +func (si SliceIndex) Key() int { + if si.xkey != si.ykey { + return -1 + } + return si.xkey +} + +// SplitKeys are the indexes for indexing into slices in the +// x and y values, respectively. These indexes may differ due to the +// insertion or removal of an element in one of the slices, causing +// all of the indexes to be shifted. If an index is -1, then that +// indicates that the element does not exist in the associated slice. +// +// Key is guaranteed to return -1 if and only if the indexes returned +// by SplitKeys are not the same. SplitKeys will never return -1 for +// both indexes. +func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } + +// MapIndex is an index operation on a map at some index Key. +type MapIndex struct{ *mapIndex } +type mapIndex struct { + pathStep + key reflect.Value +} + +func (mi MapIndex) Type() reflect.Type { return mi.typ } +func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy } +func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } + +// Key is the value of the map key. +func (mi MapIndex) Key() reflect.Value { return mi.key } + +// Indirect represents pointer indirection on the parent type. +type Indirect struct{ *indirect } +type indirect struct { + pathStep +} + +func (in Indirect) Type() reflect.Type { return in.typ } +func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } +func (in Indirect) String() string { return "*" } + +// TypeAssertion represents a type assertion on an interface. +type TypeAssertion struct{ *typeAssertion } +type typeAssertion struct { + pathStep +} + +func (ta TypeAssertion) Type() reflect.Type { return ta.typ } +func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } +func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } + +// Transform is a transformation from the parent type to the current type. +type Transform struct{ *transform } +type transform struct { + pathStep + trans *transformer +} + +func (tf Transform) Type() reflect.Type { return tf.typ } +func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } +func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } + +// Name is the name of the Transformer. +func (tf Transform) Name() string { return tf.trans.name } + +// Func is the function pointer to the transformer function. +func (tf Transform) Func() reflect.Value { return tf.trans.fnc } + +// Option returns the originally constructed Transformer option. +// The == operator can be used to detect the exact option used. +func (tf Transform) Option() Option { return tf.trans } + +// pointerPath represents a dual-stack of pointers encountered when +// recursively traversing the x and y values. This data structure supports +// detection of cycles and determining whether the cycles are equal. +// In Go, cycles can occur via pointers, slices, and maps. +// +// The pointerPath uses a map to represent a stack; where descension into a +// pointer pushes the address onto the stack, and ascension from a pointer +// pops the address from the stack. Thus, when traversing into a pointer from +// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles +// by checking whether the pointer has already been visited. The cycle detection +// uses a seperate stack for the x and y values. +// +// If a cycle is detected we need to determine whether the two pointers +// should be considered equal. The definition of equality chosen by Equal +// requires two graphs to have the same structure. To determine this, both the +// x and y values must have a cycle where the previous pointers were also +// encountered together as a pair. +// +// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and +// MapIndex with pointer information for the x and y values. +// Suppose px and py are two pointers to compare, we then search the +// Path for whether px was ever encountered in the Path history of x, and +// similarly so with py. If either side has a cycle, the comparison is only +// equal if both px and py have a cycle resulting from the same PathStep. +// +// Using a map as a stack is more performant as we can perform cycle detection +// in O(1) instead of O(N) where N is len(Path). +type pointerPath struct { + // mx is keyed by x pointers, where the value is the associated y pointer. + mx map[value.Pointer]value.Pointer + // my is keyed by y pointers, where the value is the associated x pointer. + my map[value.Pointer]value.Pointer +} + +func (p *pointerPath) Init() { + p.mx = make(map[value.Pointer]value.Pointer) + p.my = make(map[value.Pointer]value.Pointer) +} + +// Push indicates intent to descend into pointers vx and vy where +// visited reports whether either has been seen before. If visited before, +// equal reports whether both pointers were encountered together. +// Pop must be called if and only if the pointers were never visited. +// +// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map +// and be non-nil. +func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) { + px := value.PointerOf(vx) + py := value.PointerOf(vy) + _, ok1 := p.mx[px] + _, ok2 := p.my[py] + if ok1 || ok2 { + equal = p.mx[px] == py && p.my[py] == px // Pointers paired together + return equal, true + } + p.mx[px] = py + p.my[py] = px + return false, false +} + +// Pop ascends from pointers vx and vy. +func (p pointerPath) Pop(vx, vy reflect.Value) { + delete(p.mx, value.PointerOf(vx)) + delete(p.my, value.PointerOf(vy)) +} + +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report.go new file mode 100644 index 0000000..f43cd12 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report.go @@ -0,0 +1,54 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +// defaultReporter implements the reporter interface. +// +// As Equal serially calls the PushStep, Report, and PopStep methods, the +// defaultReporter constructs a tree-based representation of the compared value +// and the result of each comparison (see valueNode). +// +// When the String method is called, the FormatDiff method transforms the +// valueNode tree into a textNode tree, which is a tree-based representation +// of the textual output (see textNode). +// +// Lastly, the textNode.String method produces the final report as a string. +type defaultReporter struct { + root *valueNode + curr *valueNode +} + +func (r *defaultReporter) PushStep(ps PathStep) { + r.curr = r.curr.PushStep(ps) + if r.root == nil { + r.root = r.curr + } +} +func (r *defaultReporter) Report(rs Result) { + r.curr.Report(rs) +} +func (r *defaultReporter) PopStep() { + r.curr = r.curr.PopStep() +} + +// String provides a full report of the differences detected as a structured +// literal in pseudo-Go syntax. String may only be called after the entire tree +// has been traversed. +func (r *defaultReporter) String() string { + assert(r.root != nil && r.curr == nil) + if r.root.NumDiff == 0 { + return "" + } + ptrs := new(pointerReferences) + text := formatOptions{}.FormatDiff(r.root, ptrs) + resolveReferences(text) + return text.String() +} + +func assert(ok bool) { + if !ok { + panic("assertion failure") + } +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_compare.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_compare.go new file mode 100644 index 0000000..104bb30 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_compare.go @@ -0,0 +1,432 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + + "github.com/google/go-cmp/cmp/internal/value" +) + +// numContextRecords is the number of surrounding equal records to print. +const numContextRecords = 2 + +type diffMode byte + +const ( + diffUnknown diffMode = 0 + diffIdentical diffMode = ' ' + diffRemoved diffMode = '-' + diffInserted diffMode = '+' +) + +type typeMode int + +const ( + // emitType always prints the type. + emitType typeMode = iota + // elideType never prints the type. + elideType + // autoType prints the type only for composite kinds + // (i.e., structs, slices, arrays, and maps). + autoType +) + +type formatOptions struct { + // DiffMode controls the output mode of FormatDiff. + // + // If diffUnknown, then produce a diff of the x and y values. + // If diffIdentical, then emit values as if they were equal. + // If diffRemoved, then only emit x values (ignoring y values). + // If diffInserted, then only emit y values (ignoring x values). + DiffMode diffMode + + // TypeMode controls whether to print the type for the current node. + // + // As a general rule of thumb, we always print the type of the next node + // after an interface, and always elide the type of the next node after + // a slice or map node. + TypeMode typeMode + + // formatValueOptions are options specific to printing reflect.Values. + formatValueOptions +} + +func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { + opts.DiffMode = d + return opts +} +func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { + opts.TypeMode = t + return opts +} +func (opts formatOptions) WithVerbosity(level int) formatOptions { + opts.VerbosityLevel = level + opts.LimitVerbosity = true + return opts +} +func (opts formatOptions) verbosity() uint { + switch { + case opts.VerbosityLevel < 0: + return 0 + case opts.VerbosityLevel > 16: + return 16 // some reasonable maximum to avoid shift overflow + default: + return uint(opts.VerbosityLevel) + } +} + +const maxVerbosityPreset = 6 + +// verbosityPreset modifies the verbosity settings given an index +// between 0 and maxVerbosityPreset, inclusive. +func verbosityPreset(opts formatOptions, i int) formatOptions { + opts.VerbosityLevel = int(opts.verbosity()) + 2*i + if i > 0 { + opts.AvoidStringer = true + } + if i >= maxVerbosityPreset { + opts.PrintAddresses = true + opts.QualifiedNames = true + } + return opts +} + +// FormatDiff converts a valueNode tree into a textNode tree, where the later +// is a textual representation of the differences detected in the former. +func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { + if opts.DiffMode == diffIdentical { + opts = opts.WithVerbosity(1) + } else if opts.verbosity() < 3 { + opts = opts.WithVerbosity(3) + } + + // Check whether we have specialized formatting for this node. + // This is not necessary, but helpful for producing more readable outputs. + if opts.CanFormatDiffSlice(v) { + return opts.FormatDiffSlice(v) + } + + var parentKind reflect.Kind + if v.parent != nil && v.parent.TransformerName == "" { + parentKind = v.parent.Type.Kind() + } + + // For leaf nodes, format the value based on the reflect.Values alone. + if v.MaxDepth == 0 { + switch opts.DiffMode { + case diffUnknown, diffIdentical: + // Format Equal. + if v.NumDiff == 0 { + outx := opts.FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.FormatValue(v.ValueY, parentKind, ptrs) + if v.NumIgnored > 0 && v.NumSame == 0 { + return textEllipsis + } else if outx.Len() < outy.Len() { + return outx + } else { + return outy + } + } + + // Format unequal. + assert(opts.DiffMode == diffUnknown) + var list textList + outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i).WithTypeMode(elideType) + outx = opts2.FormatValue(v.ValueX, parentKind, ptrs) + outy = opts2.FormatValue(v.ValueY, parentKind, ptrs) + } + if outx != nil { + list = append(list, textRecord{Diff: '-', Value: outx}) + } + if outy != nil { + list = append(list, textRecord{Diff: '+', Value: outy}) + } + return opts.WithTypeMode(emitType).FormatType(v.Type, list) + case diffRemoved: + return opts.FormatValue(v.ValueX, parentKind, ptrs) + case diffInserted: + return opts.FormatValue(v.ValueY, parentKind, ptrs) + default: + panic("invalid diff mode") + } + } + + // Register slice element to support cycle detection. + if parentKind == reflect.Slice { + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true) + defer ptrs.Pop() + defer func() { out = wrapTrunkReferences(ptrRefs, out) }() + } + + // Descend into the child value node. + if v.TransformerName != "" { + out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"} + return opts.FormatType(v.Type, out) + } else { + switch k := v.Type.Kind(); k { + case reflect.Struct, reflect.Array, reflect.Slice: + out = opts.formatDiffList(v.Records, k, ptrs) + out = opts.FormatType(v.Type, out) + case reflect.Map: + // Register map to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.formatDiffList(v.Records, k, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = opts.FormatType(v.Type, out) + case reflect.Ptr: + // Register pointer to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.FormatDiff(v.Value, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = &textWrap{Prefix: "&", Value: out} + case reflect.Interface: + out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + default: + panic(fmt.Sprintf("%v cannot have children", k)) + } + return out + } +} + +func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode { + // Derive record name based on the data structure kind. + var name string + var formatKey func(reflect.Value) string + switch k { + case reflect.Struct: + name = "field" + opts = opts.WithTypeMode(autoType) + formatKey = func(v reflect.Value) string { return v.String() } + case reflect.Slice, reflect.Array: + name = "element" + opts = opts.WithTypeMode(elideType) + formatKey = func(reflect.Value) string { return "" } + case reflect.Map: + name = "entry" + opts = opts.WithTypeMode(elideType) + formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) } + } + + maxLen := -1 + if opts.LimitVerbosity { + if opts.DiffMode == diffIdentical { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + } else { + maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc... + } + opts.VerbosityLevel-- + } + + // Handle unification. + switch opts.DiffMode { + case diffIdentical, diffRemoved, diffInserted: + var list textList + var deferredEllipsis bool // Add final "..." to indicate records were dropped + for _, r := range recs { + if len(list) == maxLen { + deferredEllipsis = true + break + } + + // Elide struct fields that are zero value. + if k == reflect.Struct { + var isZero bool + switch opts.DiffMode { + case diffIdentical: + isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueY) + case diffRemoved: + isZero = value.IsZero(r.Value.ValueX) + case diffInserted: + isZero = value.IsZero(r.Value.ValueY) + } + if isZero { + continue + } + } + // Elide ignored nodes. + if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 { + deferredEllipsis = !(k == reflect.Slice || k == reflect.Array) + if !deferredEllipsis { + list.AppendEllipsis(diffStats{}) + } + continue + } + if out := opts.FormatDiff(r.Value, ptrs); out != nil { + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + } + } + if deferredEllipsis { + list.AppendEllipsis(diffStats{}) + } + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} + case diffUnknown: + default: + panic("invalid diff mode") + } + + // Handle differencing. + var numDiffs int + var list textList + var keys []reflect.Value // invariant: len(list) == len(keys) + groups := coalesceAdjacentRecords(name, recs) + maxGroup := diffStats{Name: name} + for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + + // Handle equal records. + if ds.NumDiff() == 0 { + // Compute the number of leading and trailing records to print. + var numLo, numHi int + numEqual := ds.NumIgnored + ds.NumIdentical + for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 { + if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { + break + } + numLo++ + } + for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { + if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { + break + } + numHi++ + } + if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 { + numHi++ // Avoid pointless coalescing of a single equal record + } + + // Format the equal values. + for _, r := range recs[:numLo] { + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + if numEqual > numLo+numHi { + ds.NumIdentical -= numLo + numHi + list.AppendEllipsis(ds) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + for _, r := range recs[numEqual-numHi : numEqual] { + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + recs = recs[numEqual:] + continue + } + + // Handle unequal records. + for _, r := range recs[:ds.NumDiff()] { + switch { + case opts.CanFormatDiffSlice(r.Value): + out := opts.FormatDiffSlice(r.Value) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + case r.Value.NumChildren == r.Value.MaxDepth: + outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i) + outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + } + if outx != nil { + list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) + keys = append(keys, r.Key) + } + if outy != nil { + list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) + keys = append(keys, r.Key) + } + default: + out := opts.FormatDiff(r.Value, ptrs) + list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) + } + } + recs = recs[ds.NumDiff():] + numDiffs += ds.NumDiff() + } + if maxGroup.IsZero() { + assert(len(recs) == 0) + } else { + list.AppendEllipsis(maxGroup) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + assert(len(list) == len(keys)) + + // For maps, the default formatting logic uses fmt.Stringer which may + // produce ambiguous output. Avoid calling String to disambiguate. + if k == reflect.Map { + var ambiguous bool + seenKeys := map[string]reflect.Value{} + for i, currKey := range keys { + if currKey.IsValid() { + strKey := list[i].Key + prevKey, seen := seenKeys[strKey] + if seen && prevKey.CanInterface() && currKey.CanInterface() { + ambiguous = prevKey.Interface() != currKey.Interface() + if ambiguous { + break + } + } + seenKeys[strKey] = currKey + } + } + if ambiguous { + for i, k := range keys { + if k.IsValid() { + list[i].Key = formatMapKey(k, true, ptrs) + } + } + } + } + + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} +} + +// coalesceAdjacentRecords coalesces the list of records into groups of +// adjacent equal, or unequal counts. +func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) { + var prevCase int // Arbitrary index into which case last occurred + lastStats := func(i int) *diffStats { + if prevCase != i { + groups = append(groups, diffStats{Name: name}) + prevCase = i + } + return &groups[len(groups)-1] + } + for _, r := range recs { + switch rv := r.Value; { + case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0: + lastStats(1).NumIgnored++ + case rv.NumDiff == 0: + lastStats(1).NumIdentical++ + case rv.NumDiff > 0 && !rv.ValueY.IsValid(): + lastStats(2).NumRemoved++ + case rv.NumDiff > 0 && !rv.ValueX.IsValid(): + lastStats(2).NumInserted++ + default: + lastStats(2).NumModified++ + } + } + return groups +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_references.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_references.go new file mode 100644 index 0000000..be31b33 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_references.go @@ -0,0 +1,264 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/flags" + "github.com/google/go-cmp/cmp/internal/value" +) + +const ( + pointerDelimPrefix = "⟪" + pointerDelimSuffix = "⟫" +) + +// formatPointer prints the address of the pointer. +func formatPointer(p value.Pointer, withDelims bool) string { + v := p.Uintptr() + if flags.Deterministic { + v = 0xdeadf00f // Only used for stable testing purposes + } + if withDelims { + return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix + } + return formatHex(uint64(v)) +} + +// pointerReferences is a stack of pointers visited so far. +type pointerReferences [][2]value.Pointer + +func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) { + if deref && vx.IsValid() { + vx = vx.Addr() + } + if deref && vy.IsValid() { + vy = vy.Addr() + } + switch d { + case diffUnknown, diffIdentical: + pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)} + case diffRemoved: + pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}} + case diffInserted: + pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)} + } + *ps = append(*ps, pp) + return pp +} + +func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) { + p = value.PointerOf(v) + for _, pp := range *ps { + if p == pp[0] || p == pp[1] { + return p, true + } + } + *ps = append(*ps, [2]value.Pointer{p, p}) + return p, false +} + +func (ps *pointerReferences) Pop() { + *ps = (*ps)[:len(*ps)-1] +} + +// trunkReferences is metadata for a textNode indicating that the sub-tree +// represents the value for either pointer in a pair of references. +type trunkReferences struct{ pp [2]value.Pointer } + +// trunkReference is metadata for a textNode indicating that the sub-tree +// represents the value for the given pointer reference. +type trunkReference struct{ p value.Pointer } + +// leafReference is metadata for a textNode indicating that the value is +// truncated as it refers to another part of the tree (i.e., a trunk). +type leafReference struct{ p value.Pointer } + +func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode { + switch { + case pp[0].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[1]}} + case pp[1].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + case pp[0] == pp[1]: + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + default: + return &textWrap{Value: s, Metadata: trunkReferences{pp}} + } +} +func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode { + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}} +} +func makeLeafReference(p value.Pointer, printAddress bool) textNode { + out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"} + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}} +} + +// resolveReferences walks the textNode tree searching for any leaf reference +// metadata and resolves each against the corresponding trunk references. +// Since pointer addresses in memory are not particularly readable to the user, +// it replaces each pointer value with an arbitrary and unique reference ID. +func resolveReferences(s textNode) { + var walkNodes func(textNode, func(textNode)) + walkNodes = func(s textNode, f func(textNode)) { + f(s) + switch s := s.(type) { + case *textWrap: + walkNodes(s.Value, f) + case textList: + for _, r := range s { + walkNodes(r.Value, f) + } + } + } + + // Collect all trunks and leaves with reference metadata. + var trunks, leaves []*textWrap + walkNodes(s, func(s textNode) { + if s, ok := s.(*textWrap); ok { + switch s.Metadata.(type) { + case leafReference: + leaves = append(leaves, s) + case trunkReference, trunkReferences: + trunks = append(trunks, s) + } + } + }) + + // No leaf references to resolve. + if len(leaves) == 0 { + return + } + + // Collect the set of all leaf references to resolve. + leafPtrs := make(map[value.Pointer]bool) + for _, leaf := range leaves { + leafPtrs[leaf.Metadata.(leafReference).p] = true + } + + // Collect the set of trunk pointers that are always paired together. + // This allows us to assign a single ID to both pointers for brevity. + // If a pointer in a pair ever occurs by itself or as a different pair, + // then the pair is broken. + pairedTrunkPtrs := make(map[value.Pointer]value.Pointer) + unpair := func(p value.Pointer) { + if !pairedTrunkPtrs[p].IsNil() { + pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half + } + pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + unpair(p.p) // standalone pointer cannot be part of a pair + case trunkReferences: + p0, ok0 := pairedTrunkPtrs[p.pp[0]] + p1, ok1 := pairedTrunkPtrs[p.pp[1]] + switch { + case !ok0 && !ok1: + // Register the newly seen pair. + pairedTrunkPtrs[p.pp[0]] = p.pp[1] + pairedTrunkPtrs[p.pp[1]] = p.pp[0] + case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]: + // Exact pair already seen; do nothing. + default: + // Pair conflicts with some other pair; break all pairs. + unpair(p.pp[0]) + unpair(p.pp[1]) + } + } + } + + // Correlate each pointer referenced by leaves to a unique identifier, + // and print the IDs for each trunk that matches those pointers. + var nextID uint + ptrIDs := make(map[value.Pointer]uint) + newID := func() uint { + id := nextID + nextID++ + return id + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + if print := leafPtrs[p.p]; print { + id, ok := ptrIDs[p.p] + if !ok { + id = newID() + ptrIDs[p.p] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } + case trunkReferences: + print0 := leafPtrs[p.pp[0]] + print1 := leafPtrs[p.pp[1]] + if print0 || print1 { + id0, ok0 := ptrIDs[p.pp[0]] + id1, ok1 := ptrIDs[p.pp[1]] + isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0] + if isPair { + var id uint + assert(ok0 == ok1) // must be seen together or not at all + if ok0 { + assert(id0 == id1) // must have the same ID + id = id0 + } else { + id = newID() + ptrIDs[p.pp[0]] = id + ptrIDs[p.pp[1]] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } else { + if print0 && !ok0 { + id0 = newID() + ptrIDs[p.pp[0]] = id0 + } + if print1 && !ok1 { + id1 = newID() + ptrIDs[p.pp[1]] = id1 + } + switch { + case print0 && print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1)) + case print0: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)) + case print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1)) + } + } + } + } + } + + // Update all leaf references with the unique identifier. + for _, leaf := range leaves { + if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok { + leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id)) + } + } +} + +func formatReference(id uint) string { + return fmt.Sprintf("ref#%d", id) +} + +func updateReferencePrefix(prefix, ref string) string { + if prefix == "" { + return pointerDelimPrefix + ref + pointerDelimSuffix + } + suffix := strings.TrimPrefix(prefix, pointerDelimPrefix) + return pointerDelimPrefix + ref + ": " + suffix +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_reflect.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_reflect.go new file mode 100644 index 0000000..33f0357 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_reflect.go @@ -0,0 +1,402 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" +) + +type formatValueOptions struct { + // AvoidStringer controls whether to avoid calling custom stringer + // methods like error.Error or fmt.Stringer.String. + AvoidStringer bool + + // PrintAddresses controls whether to print the address of all pointers, + // slice elements, and maps. + PrintAddresses bool + + // QualifiedNames controls whether FormatType uses the fully qualified name + // (including the full package path as opposed to just the package name). + QualifiedNames bool + + // VerbosityLevel controls the amount of output to produce. + // A higher value produces more output. A value of zero or lower produces + // no output (represented using an ellipsis). + // If LimitVerbosity is false, then the level is treated as infinite. + VerbosityLevel int + + // LimitVerbosity specifies that formatting should respect VerbosityLevel. + LimitVerbosity bool +} + +// FormatType prints the type as if it were wrapping s. +// This may return s as-is depending on the current type and TypeMode mode. +func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { + // Check whether to emit the type or not. + switch opts.TypeMode { + case autoType: + switch t.Kind() { + case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: + if s.Equal(textNil) { + return s + } + default: + return s + } + if opts.DiffMode == diffIdentical { + return s // elide type for identical nodes + } + case elideType: + return s + } + + // Determine the type label, applying special handling for unnamed types. + typeName := value.TypeString(t, opts.QualifiedNames) + if t.Name() == "" { + // According to Go grammar, certain type literals contain symbols that + // do not strongly bind to the next lexicographical token (e.g., *T). + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.Ptr: + typeName = "(" + typeName + ")" + } + } + return &textWrap{Prefix: typeName, Value: wrapParens(s)} +} + +// wrapParens wraps s with a set of parenthesis, but avoids it if the +// wrapped node itself is already surrounded by a pair of parenthesis or braces. +// It handles unwrapping one level of pointer-reference nodes. +func wrapParens(s textNode) textNode { + var refNode *textWrap + if s2, ok := s.(*textWrap); ok { + // Unwrap a single pointer reference node. + switch s2.Metadata.(type) { + case leafReference, trunkReference, trunkReferences: + refNode = s2 + if s3, ok := refNode.Value.(*textWrap); ok { + s2 = s3 + } + } + + // Already has delimiters that make parenthesis unnecessary. + hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")") + hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}") + if hasParens || hasBraces { + return s + } + } + if refNode != nil { + refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"} + return s + } + return &textWrap{Prefix: "(", Value: s, Suffix: ")"} +} + +// FormatValue prints the reflect.Value, taking extra care to avoid descending +// into pointers already in ptrs. As pointers are visited, ptrs is also updated. +func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { + if !v.IsValid() { + return nil + } + t := v.Type() + + // Check slice element for cycles. + if parentKind == reflect.Slice { + ptrRef, visited := ptrs.Push(v.Addr()) + if visited { + return makeLeafReference(ptrRef, false) + } + defer ptrs.Pop() + defer func() { out = wrapTrunkReference(ptrRef, false, out) }() + } + + // Check whether there is an Error or String method to call. + if !opts.AvoidStringer && v.CanInterface() { + // Avoid calling Error or String methods on nil receivers since many + // implementations crash when doing so. + if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { + var prefix, strVal string + func() { + // Swallow and ignore any panics from String or Error. + defer func() { recover() }() + switch v := v.Interface().(type) { + case error: + strVal = v.Error() + prefix = "e" + case fmt.Stringer: + strVal = v.String() + prefix = "s" + } + }() + if prefix != "" { + return opts.formatString(prefix, strVal) + } + } + } + + // Check whether to explicitly wrap the result with the type. + var skipType bool + defer func() { + if !skipType { + out = opts.FormatType(t, out) + } + }() + + switch t.Kind() { + case reflect.Bool: + return textLine(fmt.Sprint(v.Bool())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return textLine(fmt.Sprint(v.Int())) + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uint8: + if parentKind == reflect.Slice || parentKind == reflect.Array { + return textLine(formatHex(v.Uint())) + } + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uintptr: + return textLine(formatHex(v.Uint())) + case reflect.Float32, reflect.Float64: + return textLine(fmt.Sprint(v.Float())) + case reflect.Complex64, reflect.Complex128: + return textLine(fmt.Sprint(v.Complex())) + case reflect.String: + return opts.formatString("", v.String()) + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + return textLine(formatPointer(value.PointerOf(v), true)) + case reflect.Struct: + var list textList + v := makeAddressable(v) // needed for retrieveUnexportedField + maxLen := v.NumField() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + for i := 0; i < v.NumField(); i++ { + vv := v.Field(i) + if value.IsZero(vv) { + continue // Elide fields with zero values + } + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sf := t.Field(i) + if supportExporters && !isExported(sf.Name) { + vv = retrieveUnexportedField(v, sf, true) + } + s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) + list = append(list, textRecord{Key: sf.Name, Value: s}) + } + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} + case reflect.Slice: + if v.IsNil() { + return textNil + } + + // Check whether this is a []byte of text data. + if t.Elem() == reflect.TypeOf(byte(0)) { + b := v.Bytes() + isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) && unicode.IsSpace(r) } + if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { + out = opts.formatString("", string(b)) + return opts.WithTypeMode(emitType).FormatType(t, out) + } + } + + fallthrough + case reflect.Array: + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + var list textList + for i := 0; i < v.Len(); i++ { + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs) + list = append(list, textRecord{Value: s}) + } + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if t.Kind() == reflect.Slice && opts.PrintAddresses { + header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap()) + out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out} + } + return out + case reflect.Map: + if v.IsNil() { + return textNil + } + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + return makeLeafReference(ptrRef, opts.PrintAddresses) + } + defer ptrs.Pop() + + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } + var list textList + for _, k := range value.SortKeys(v.MapKeys()) { + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sk := formatMapKey(k, false, ptrs) + sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs) + list = append(list, textRecord{Key: sk, Value: sv}) + } + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + return out + case reflect.Ptr: + if v.IsNil() { + return textNil + } + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + out = makeLeafReference(ptrRef, opts.PrintAddresses) + return &textWrap{Prefix: "&", Value: out} + } + defer ptrs.Pop() + + skipType = true // Let the underlying value print the type instead + out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + out = &textWrap{Prefix: "&", Value: out} + return out + case reflect.Interface: + if v.IsNil() { + return textNil + } + // Interfaces accept different concrete types, + // so configure the underlying value to explicitly print the type. + skipType = true // Print the concrete type instead + return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) + default: + panic(fmt.Sprintf("%v kind not handled", v.Kind())) + } +} + +func (opts formatOptions) formatString(prefix, s string) textNode { + maxLen := len(s) + maxLines := strings.Count(s, "\n") + 1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... + maxLines = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + } + + // For multiline strings, use the triple-quote syntax, + // but only use it when printing removed or inserted nodes since + // we only want the extra verbosity for those cases. + lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n") + isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+') + for i := 0; i < len(lines) && isTripleQuoted; i++ { + lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + line := lines[i] + isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen + } + if isTripleQuoted { + var list textList + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) + for i, line := range lines { + if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 { + comment := commentString(fmt.Sprintf("%d elided lines", numElided)) + list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment}) + break + } + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true}) + } + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) + return &textWrap{Prefix: "(", Value: list, Suffix: ")"} + } + + // Format the string as a single-line quoted string. + if len(s) > maxLen+len(textEllipsis) { + return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis)) + } + return textLine(prefix + formatString(s)) +} + +// formatMapKey formats v as if it were a map key. +// The result is guaranteed to be a single line. +func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { + var opts formatOptions + opts.DiffMode = diffIdentical + opts.TypeMode = elideType + opts.PrintAddresses = disambiguate + opts.AvoidStringer = disambiguate + opts.QualifiedNames = disambiguate + opts.VerbosityLevel = maxVerbosityPreset + opts.LimitVerbosity = true + s := opts.FormatValue(v, reflect.Map, ptrs).String() + return strings.TrimSpace(s) +} + +// formatString prints s as a double-quoted or backtick-quoted string. +func formatString(s string) string { + // Use quoted string if it the same length as a raw string literal. + // Otherwise, attempt to use the raw string form. + qs := strconv.Quote(s) + if len(qs) == 1+len(s)+1 { + return qs + } + + // Disallow newlines to ensure output is a single line. + // Only allow printable runes for readability purposes. + rawInvalid := func(r rune) bool { + return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') + } + if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 { + return "`" + s + "`" + } + return qs +} + +// formatHex prints u as a hexadecimal integer in Go notation. +func formatHex(u uint64) string { + var f string + switch { + case u <= 0xff: + f = "0x%02x" + case u <= 0xffff: + f = "0x%04x" + case u <= 0xffffff: + f = "0x%06x" + case u <= 0xffffffff: + f = "0x%08x" + case u <= 0xffffffffff: + f = "0x%010x" + case u <= 0xffffffffffff: + f = "0x%012x" + case u <= 0xffffffffffffff: + f = "0x%014x" + case u <= 0xffffffffffffffff: + f = "0x%016x" + } + return fmt.Sprintf(f, u) +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_slices.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_slices.go new file mode 100644 index 0000000..168f92f --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_slices.go @@ -0,0 +1,465 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/diff" +) + +// CanFormatDiffSlice reports whether we support custom formatting for nodes +// that are slices of primitive kinds or strings. +func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { + switch { + case opts.DiffMode != diffUnknown: + return false // Must be formatting in diff mode + case v.NumDiff == 0: + return false // No differences detected + case !v.ValueX.IsValid() || !v.ValueY.IsValid(): + return false // Both values must be valid + case v.NumIgnored > 0: + return false // Some ignore option was used + case v.NumTransformed > 0: + return false // Some transform option was used + case v.NumCompared > 1: + return false // More than one comparison was used + case v.NumCompared == 1 && v.Type.Name() != "": + // The need for cmp to check applicability of options on every element + // in a slice is a significant performance detriment for large []byte. + // The workaround is to specify Comparer(bytes.Equal), + // which enables cmp to compare []byte more efficiently. + // If they differ, we still want to provide batched diffing. + // The logic disallows named types since they tend to have their own + // String method, with nicer formatting than what this provides. + return false + } + + // Check whether this is an interface with the same concrete types. + t := v.Type + vx, vy := v.ValueX, v.ValueY + if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() { + vx, vy = vx.Elem(), vy.Elem() + t = vx.Type() + } + + // Check whether we provide specialized diffing for this type. + switch t.Kind() { + case reflect.String: + case reflect.Array, reflect.Slice: + // Only slices of primitive types have specialized handling. + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + default: + return false + } + + // Both slice values have to be non-empty. + if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) { + return false + } + + // If a sufficient number of elements already differ, + // use specialized formatting even if length requirement is not met. + if v.NumDiff > v.NumSame { + return true + } + default: + return false + } + + // Use specialized string diffing for longer slices or strings. + const minLength = 64 + return vx.Len() >= minLength && vy.Len() >= minLength +} + +// FormatDiffSlice prints a diff for the slices (or strings) represented by v. +// This provides custom-tailored logic to make printing of differences in +// textual strings and slices of primitive kinds more readable. +func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { + assert(opts.DiffMode == diffUnknown) + t, vx, vy := v.Type, v.ValueX, v.ValueY + if t.Kind() == reflect.Interface { + vx, vy = vx.Elem(), vy.Elem() + t = vx.Type() + opts = opts.WithTypeMode(emitType) + } + + // Auto-detect the type of the data. + var isLinedText, isText, isBinary bool + var sx, sy string + switch { + case t.Kind() == reflect.String: + sx, sy = vx.String(), vy.String() + isText = true // Initial estimate, verify later + case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)): + sx, sy = string(vx.Bytes()), string(vy.Bytes()) + isBinary = true // Initial estimate, verify later + case t.Kind() == reflect.Array: + // Arrays need to be addressable for slice operations to work. + vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() + vx2.Set(vx) + vy2.Set(vy) + vx, vy = vx2, vy2 + } + if isText || isBinary { + var numLines, lastLineIdx, maxLineLen int + isBinary = !utf8.ValidString(sx) || !utf8.ValidString(sy) + for i, r := range sx + sy { + if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError { + isBinary = true + break + } + if r == '\n' { + if maxLineLen < i-lastLineIdx { + maxLineLen = i - lastLineIdx + } + lastLineIdx = i + 1 + numLines++ + } + } + isText = !isBinary + isLinedText = isText && numLines >= 4 && maxLineLen <= 1024 + } + + // Format the string into printable records. + var list textList + var delim string + switch { + // If the text appears to be multi-lined text, + // then perform differencing across individual lines. + case isLinedText: + ssx := strings.Split(sx, "\n") + ssy := strings.Split(sy, "\n") + list = opts.formatDiffSlice( + reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", + func(v reflect.Value, d diffMode) textRecord { + s := formatString(v.Index(0).String()) + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + delim = "\n" + + // If possible, use a custom triple-quote (""") syntax for printing + // differences in a string literal. This format is more readable, + // but has edge-cases where differences are visually indistinguishable. + // This format is avoided under the following conditions: + // • A line starts with `"""` + // • A line starts with "..." + // • A line contains non-printable characters + // • Adjacent different lines differ only by whitespace + // + // For example: + // """ + // ... // 3 identical lines + // foo + // bar + // - baz + // + BAZ + // """ + isTripleQuoted := true + prevRemoveLines := map[string]bool{} + prevInsertLines := map[string]bool{} + var list2 textList + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + for _, r := range list { + if !r.Value.Equal(textEllipsis) { + line, _ := strconv.Unquote(string(r.Value.(textLine))) + line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + normLine := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 // drop whitespace to avoid visually indistinguishable output + } + return r + }, line) + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" + switch r.Diff { + case diffRemoved: + isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine] + prevRemoveLines[normLine] = true + case diffInserted: + isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine] + prevInsertLines[normLine] = true + } + if !isTripleQuoted { + break + } + r.Value = textLine(line) + r.ElideComma = true + } + if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group + prevRemoveLines = map[string]bool{} + prevInsertLines = map[string]bool{} + } + list2 = append(list2, r) + } + if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 { + list2 = list2[:len(list2)-1] // elide single empty line at the end + } + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + if isTripleQuoted { + var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"} + switch t.Kind() { + case reflect.String: + if t != reflect.TypeOf(string("")) { + out = opts.FormatType(t, out) + } + case reflect.Slice: + // Always emit type for slices since the triple-quote syntax + // looks like a string (not a slice). + opts = opts.WithTypeMode(emitType) + out = opts.FormatType(t, out) + } + return out + } + + // If the text appears to be single-lined text, + // then perform differencing in approximately fixed-sized chunks. + // The output is printed as quoted strings. + case isText: + list = opts.formatDiffSlice( + reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", + func(v reflect.Value, d diffMode) textRecord { + s := formatString(v.String()) + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + delim = "" + + // If the text appears to be binary data, + // then perform differencing in approximately fixed-sized chunks. + // The output is inspired by hexdump. + case isBinary: + list = opts.formatDiffSlice( + reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte", + func(v reflect.Value, d diffMode) textRecord { + var ss []string + for i := 0; i < v.Len(); i++ { + ss = append(ss, formatHex(v.Index(i).Uint())) + } + s := strings.Join(ss, ", ") + comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String()))) + return textRecord{Diff: d, Value: textLine(s), Comment: comment} + }, + ) + + // For all other slices of primitive types, + // then perform differencing in approximately fixed-sized chunks. + // The size of each chunk depends on the width of the element kind. + default: + var chunkSize int + if t.Elem().Kind() == reflect.Bool { + chunkSize = 16 + } else { + switch t.Elem().Bits() { + case 8: + chunkSize = 16 + case 16: + chunkSize = 12 + case 32: + chunkSize = 8 + default: + chunkSize = 8 + } + } + list = opts.formatDiffSlice( + vx, vy, chunkSize, t.Elem().Kind().String(), + func(v reflect.Value, d diffMode) textRecord { + var ss []string + for i := 0; i < v.Len(); i++ { + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + ss = append(ss, fmt.Sprint(v.Index(i).Int())) + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + ss = append(ss, fmt.Sprint(v.Index(i).Uint())) + case reflect.Uint8, reflect.Uintptr: + ss = append(ss, formatHex(v.Index(i).Uint())) + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + ss = append(ss, fmt.Sprint(v.Index(i).Interface())) + } + } + s := strings.Join(ss, ", ") + return textRecord{Diff: d, Value: textLine(s)} + }, + ) + } + + // Wrap the output with appropriate type information. + var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if !isText { + // The "{...}" byte-sequence literal is not valid Go syntax for strings. + // Emit the type for extra clarity (e.g. "string{...}"). + if t.Kind() == reflect.String { + opts = opts.WithTypeMode(emitType) + } + return opts.FormatType(t, out) + } + switch t.Kind() { + case reflect.String: + out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != reflect.TypeOf(string("")) { + out = opts.FormatType(t, out) + } + case reflect.Slice: + out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != reflect.TypeOf([]byte(nil)) { + out = opts.FormatType(t, out) + } + } + return out +} + +// formatASCII formats s as an ASCII string. +// This is useful for printing binary strings in a semi-legible way. +func formatASCII(s string) string { + b := bytes.Repeat([]byte{'.'}, len(s)) + for i := 0; i < len(s); i++ { + if ' ' <= s[i] && s[i] <= '~' { + b[i] = s[i] + } + } + return string(b) +} + +func (opts formatOptions) formatDiffSlice( + vx, vy reflect.Value, chunkSize int, name string, + makeRec func(reflect.Value, diffMode) textRecord, +) (list textList) { + es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result { + return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface()) + }) + + appendChunks := func(v reflect.Value, d diffMode) int { + n0 := v.Len() + for v.Len() > 0 { + n := chunkSize + if n > v.Len() { + n = v.Len() + } + list = append(list, makeRec(v.Slice(0, n), d)) + v = v.Slice(n, v.Len()) + } + return n0 - v.Len() + } + + var numDiffs int + maxLen := -1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + opts.VerbosityLevel-- + } + + groups := coalesceAdjacentEdits(name, es) + groups = coalesceInterveningIdentical(groups, chunkSize/4) + maxGroup := diffStats{Name: name} + for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + + // Print equal. + if ds.NumDiff() == 0 { + // Compute the number of leading and trailing equal bytes to print. + var numLo, numHi int + numEqual := ds.NumIgnored + ds.NumIdentical + for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 { + numLo++ + } + for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { + numHi++ + } + if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 { + numHi = numEqual - numLo // Avoid pointless coalescing of single equal row + } + + // Print the equal bytes. + appendChunks(vx.Slice(0, numLo), diffIdentical) + if numEqual > numLo+numHi { + ds.NumIdentical -= numLo + numHi + list.AppendEllipsis(ds) + } + appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical) + vx = vx.Slice(numEqual, vx.Len()) + vy = vy.Slice(numEqual, vy.Len()) + continue + } + + // Print unequal. + len0 := len(list) + nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) + vx = vx.Slice(nx, vx.Len()) + ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) + vy = vy.Slice(ny, vy.Len()) + numDiffs += len(list) - len0 + } + if maxGroup.IsZero() { + assert(vx.Len() == 0 && vy.Len() == 0) + } else { + list.AppendEllipsis(maxGroup) + } + return list +} + +// coalesceAdjacentEdits coalesces the list of edits into groups of adjacent +// equal or unequal counts. +func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { + var prevCase int // Arbitrary index into which case last occurred + lastStats := func(i int) *diffStats { + if prevCase != i { + groups = append(groups, diffStats{Name: name}) + prevCase = i + } + return &groups[len(groups)-1] + } + for _, e := range es { + switch e { + case diff.Identity: + lastStats(1).NumIdentical++ + case diff.UniqueX: + lastStats(2).NumRemoved++ + case diff.UniqueY: + lastStats(2).NumInserted++ + case diff.Modified: + lastStats(2).NumModified++ + } + } + return groups +} + +// coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) +// equal groups into adjacent unequal groups that currently result in a +// dual inserted/removed printout. This acts as a high-pass filter to smooth +// out high-frequency changes within the windowSize. +func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { + groups, groupsOrig := groups[:0], groups + for i, ds := range groupsOrig { + if len(groups) >= 2 && ds.NumDiff() > 0 { + prev := &groups[len(groups)-2] // Unequal group + curr := &groups[len(groups)-1] // Equal group + next := &groupsOrig[i] // Unequal group + hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 + hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 + if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { + *prev = prev.Append(*curr).Append(*next) + groups = groups[:len(groups)-1] // Truncate off equal group + continue + } + } + groups = append(groups, ds) + } + return groups +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_text.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_text.go new file mode 100644 index 0000000..0fd46d7 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_text.go @@ -0,0 +1,431 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "bytes" + "fmt" + "math/rand" + "strings" + "time" + "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/flags" +) + +var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 + +const maxColumnLength = 80 + +type indentMode int + +func (n indentMode) appendIndent(b []byte, d diffMode) []byte { + // The output of Diff is documented as being unstable to provide future + // flexibility in changing the output for more humanly readable reports. + // This logic intentionally introduces instability to the exact output + // so that users can detect accidental reliance on stability early on, + // rather than much later when an actual change to the format occurs. + if flags.Deterministic || randBool { + // Use regular spaces (U+0020). + switch d { + case diffUnknown, diffIdentical: + b = append(b, " "...) + case diffRemoved: + b = append(b, "- "...) + case diffInserted: + b = append(b, "+ "...) + } + } else { + // Use non-breaking spaces (U+00a0). + switch d { + case diffUnknown, diffIdentical: + b = append(b, "  "...) + case diffRemoved: + b = append(b, "- "...) + case diffInserted: + b = append(b, "+ "...) + } + } + return repeatCount(n).appendChar(b, '\t') +} + +type repeatCount int + +func (n repeatCount) appendChar(b []byte, c byte) []byte { + for ; n > 0; n-- { + b = append(b, c) + } + return b +} + +// textNode is a simplified tree-based representation of structured text. +// Possible node types are textWrap, textList, or textLine. +type textNode interface { + // Len reports the length in bytes of a single-line version of the tree. + // Nested textRecord.Diff and textRecord.Comment fields are ignored. + Len() int + // Equal reports whether the two trees are structurally identical. + // Nested textRecord.Diff and textRecord.Comment fields are compared. + Equal(textNode) bool + // String returns the string representation of the text tree. + // It is not guaranteed that len(x.String()) == x.Len(), + // nor that x.String() == y.String() implies that x.Equal(y). + String() string + + // formatCompactTo formats the contents of the tree as a single-line string + // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment + // fields are ignored. + // + // However, not all nodes in the tree should be collapsed as a single-line. + // If a node can be collapsed as a single-line, it is replaced by a textLine + // node. Since the top-level node cannot replace itself, this also returns + // the current node itself. + // + // This does not mutate the receiver. + formatCompactTo([]byte, diffMode) ([]byte, textNode) + // formatExpandedTo formats the contents of the tree as a multi-line string + // to the provided buffer. In order for column alignment to operate well, + // formatCompactTo must be called before calling formatExpandedTo. + formatExpandedTo([]byte, diffMode, indentMode) []byte +} + +// textWrap is a wrapper that concatenates a prefix and/or a suffix +// to the underlying node. +type textWrap struct { + Prefix string // e.g., "bytes.Buffer{" + Value textNode // textWrap | textList | textLine + Suffix string // e.g., "}" + Metadata interface{} // arbitrary metadata; has no effect on formatting +} + +func (s *textWrap) Len() int { + return len(s.Prefix) + s.Value.Len() + len(s.Suffix) +} +func (s1 *textWrap) Equal(s2 textNode) bool { + if s2, ok := s2.(*textWrap); ok { + return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix + } + return false +} +func (s *textWrap) String() string { + var d diffMode + var n indentMode + _, s2 := s.formatCompactTo(nil, d) + b := n.appendIndent(nil, d) // Leading indent + b = s2.formatExpandedTo(b, d, n) // Main body + b = append(b, '\n') // Trailing newline + return string(b) +} +func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + n0 := len(b) // Original buffer length + b = append(b, s.Prefix...) + b, s.Value = s.Value.formatCompactTo(b, d) + b = append(b, s.Suffix...) + if _, ok := s.Value.(textLine); ok { + return b, textLine(b[n0:]) + } + return b, s +} +func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { + b = append(b, s.Prefix...) + b = s.Value.formatExpandedTo(b, d, n) + b = append(b, s.Suffix...) + return b +} + +// textList is a comma-separated list of textWrap or textLine nodes. +// The list may be formatted as multi-lines or single-line at the discretion +// of the textList.formatCompactTo method. +type textList []textRecord +type textRecord struct { + Diff diffMode // e.g., 0 or '-' or '+' + Key string // e.g., "MyField" + Value textNode // textWrap | textLine + ElideComma bool // avoid trailing comma + Comment fmt.Stringer // e.g., "6 identical fields" +} + +// AppendEllipsis appends a new ellipsis node to the list if none already +// exists at the end. If cs is non-zero it coalesces the statistics with the +// previous diffStats. +func (s *textList) AppendEllipsis(ds diffStats) { + hasStats := !ds.IsZero() + if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { + if hasStats { + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds}) + } else { + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true}) + } + return + } + if hasStats { + (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds) + } +} + +func (s textList) Len() (n int) { + for i, r := range s { + n += len(r.Key) + if r.Key != "" { + n += len(": ") + } + n += r.Value.Len() + if i < len(s)-1 { + n += len(", ") + } + } + return n +} + +func (s1 textList) Equal(s2 textNode) bool { + if s2, ok := s2.(textList); ok { + if len(s1) != len(s2) { + return false + } + for i := range s1 { + r1, r2 := s1[i], s2[i] + if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) { + return false + } + } + return true + } + return false +} + +func (s textList) String() string { + return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String() +} + +func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + s = append(textList(nil), s...) // Avoid mutating original + + // Determine whether we can collapse this list as a single line. + n0 := len(b) // Original buffer length + var multiLine bool + for i, r := range s { + if r.Diff == diffInserted || r.Diff == diffRemoved { + multiLine = true + } + b = append(b, r.Key...) + if r.Key != "" { + b = append(b, ": "...) + } + b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff) + if _, ok := s[i].Value.(textLine); !ok { + multiLine = true + } + if r.Comment != nil { + multiLine = true + } + if i < len(s)-1 { + b = append(b, ", "...) + } + } + // Force multi-lined output when printing a removed/inserted node that + // is sufficiently long. + if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength { + multiLine = true + } + if !multiLine { + return b, textLine(b[n0:]) + } + return b, s +} + +func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { + alignKeyLens := s.alignLens( + func(r textRecord) bool { + _, isLine := r.Value.(textLine) + return r.Key == "" || !isLine + }, + func(r textRecord) int { return utf8.RuneCountInString(r.Key) }, + ) + alignValueLens := s.alignLens( + func(r textRecord) bool { + _, isLine := r.Value.(textLine) + return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil + }, + func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) }, + ) + + // Format lists of simple lists in a batched form. + // If the list is sequence of only textLine values, + // then batch multiple values on a single line. + var isSimple bool + for _, r := range s { + _, isLine := r.Value.(textLine) + isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil + if !isSimple { + break + } + } + if isSimple { + n++ + var batch []byte + emitBatch := func() { + if len(batch) > 0 { + b = n.appendIndent(append(b, '\n'), d) + b = append(b, bytes.TrimRight(batch, " ")...) + batch = batch[:0] + } + } + for _, r := range s { + line := r.Value.(textLine) + if len(batch)+len(line)+len(", ") > maxColumnLength { + emitBatch() + } + batch = append(batch, line...) + batch = append(batch, ", "...) + } + emitBatch() + n-- + return n.appendIndent(append(b, '\n'), d) + } + + // Format the list as a multi-lined output. + n++ + for i, r := range s { + b = n.appendIndent(append(b, '\n'), d|r.Diff) + if r.Key != "" { + b = append(b, r.Key+": "...) + } + b = alignKeyLens[i].appendChar(b, ' ') + + b = r.Value.formatExpandedTo(b, d|r.Diff, n) + if !r.ElideComma { + b = append(b, ',') + } + b = alignValueLens[i].appendChar(b, ' ') + + if r.Comment != nil { + b = append(b, " // "+r.Comment.String()...) + } + } + n-- + + return n.appendIndent(append(b, '\n'), d) +} + +func (s textList) alignLens( + skipFunc func(textRecord) bool, + lenFunc func(textRecord) int, +) []repeatCount { + var startIdx, endIdx, maxLen int + lens := make([]repeatCount, len(s)) + for i, r := range s { + if skipFunc(r) { + for j := startIdx; j < endIdx && j < len(s); j++ { + lens[j] = repeatCount(maxLen - lenFunc(s[j])) + } + startIdx, endIdx, maxLen = i+1, i+1, 0 + } else { + if maxLen < lenFunc(r) { + maxLen = lenFunc(r) + } + endIdx = i + 1 + } + } + for j := startIdx; j < endIdx && j < len(s); j++ { + lens[j] = repeatCount(maxLen - lenFunc(s[j])) + } + return lens +} + +// textLine is a single-line segment of text and is always a leaf node +// in the textNode tree. +type textLine []byte + +var ( + textNil = textLine("nil") + textEllipsis = textLine("...") +) + +func (s textLine) Len() int { + return len(s) +} +func (s1 textLine) Equal(s2 textNode) bool { + if s2, ok := s2.(textLine); ok { + return bytes.Equal([]byte(s1), []byte(s2)) + } + return false +} +func (s textLine) String() string { + return string(s) +} +func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { + return append(b, s...), s +} +func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte { + return append(b, s...) +} + +type diffStats struct { + Name string + NumIgnored int + NumIdentical int + NumRemoved int + NumInserted int + NumModified int +} + +func (s diffStats) IsZero() bool { + s.Name = "" + return s == diffStats{} +} + +func (s diffStats) NumDiff() int { + return s.NumRemoved + s.NumInserted + s.NumModified +} + +func (s diffStats) Append(ds diffStats) diffStats { + assert(s.Name == ds.Name) + s.NumIgnored += ds.NumIgnored + s.NumIdentical += ds.NumIdentical + s.NumRemoved += ds.NumRemoved + s.NumInserted += ds.NumInserted + s.NumModified += ds.NumModified + return s +} + +// String prints a humanly-readable summary of coalesced records. +// +// Example: +// diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" +func (s diffStats) String() string { + var ss []string + var sum int + labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"} + counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified} + for i, n := range counts { + if n > 0 { + ss = append(ss, fmt.Sprintf("%d %v", n, labels[i])) + } + sum += n + } + + // Pluralize the name (adjusting for some obscure English grammar rules). + name := s.Name + if sum > 1 { + name += "s" + if strings.HasSuffix(name, "ys") { + name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" + } + } + + // Format the list according to English grammar (with Oxford comma). + switch n := len(ss); n { + case 0: + return "" + case 1, 2: + return strings.Join(ss, " and ") + " " + name + default: + return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name + } +} + +type commentString string + +func (s commentString) String() string { return string(s) } diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_value.go b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_value.go new file mode 100644 index 0000000..668d470 --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/report_value.go @@ -0,0 +1,121 @@ +// Copyright 2019, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import "reflect" + +// valueNode represents a single node within a report, which is a +// structured representation of the value tree, containing information +// regarding which nodes are equal or not. +type valueNode struct { + parent *valueNode + + Type reflect.Type + ValueX reflect.Value + ValueY reflect.Value + + // NumSame is the number of leaf nodes that are equal. + // All descendants are equal only if NumDiff is 0. + NumSame int + // NumDiff is the number of leaf nodes that are not equal. + NumDiff int + // NumIgnored is the number of leaf nodes that are ignored. + NumIgnored int + // NumCompared is the number of leaf nodes that were compared + // using an Equal method or Comparer function. + NumCompared int + // NumTransformed is the number of non-leaf nodes that were transformed. + NumTransformed int + // NumChildren is the number of transitive descendants of this node. + // This counts from zero; thus, leaf nodes have no descendants. + NumChildren int + // MaxDepth is the maximum depth of the tree. This counts from zero; + // thus, leaf nodes have a depth of zero. + MaxDepth int + + // Records is a list of struct fields, slice elements, or map entries. + Records []reportRecord // If populated, implies Value is not populated + + // Value is the result of a transformation, pointer indirect, of + // type assertion. + Value *valueNode // If populated, implies Records is not populated + + // TransformerName is the name of the transformer. + TransformerName string // If non-empty, implies Value is populated +} +type reportRecord struct { + Key reflect.Value // Invalid for slice element + Value *valueNode +} + +func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { + vx, vy := ps.Values() + child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} + switch s := ps.(type) { + case StructField: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) + case SliceIndex: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Value: child}) + case MapIndex: + assert(parent.Value == nil) + parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) + case Indirect: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + case TypeAssertion: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + case Transform: + assert(parent.Value == nil && parent.Records == nil) + parent.Value = child + parent.TransformerName = s.Name() + parent.NumTransformed++ + default: + assert(parent == nil) // Must be the root step + } + return child +} + +func (r *valueNode) Report(rs Result) { + assert(r.MaxDepth == 0) // May only be called on leaf nodes + + if rs.ByIgnore() { + r.NumIgnored++ + } else { + if rs.Equal() { + r.NumSame++ + } else { + r.NumDiff++ + } + } + assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) + + if rs.ByMethod() { + r.NumCompared++ + } + if rs.ByFunc() { + r.NumCompared++ + } + assert(r.NumCompared <= 1) +} + +func (child *valueNode) PopStep() (parent *valueNode) { + if child.parent == nil { + return nil + } + parent = child.parent + parent.NumSame += child.NumSame + parent.NumDiff += child.NumDiff + parent.NumIgnored += child.NumIgnored + parent.NumCompared += child.NumCompared + parent.NumTransformed += child.NumTransformed + parent.NumChildren += child.NumChildren + 1 + if parent.MaxDepth < child.MaxDepth+1 { + parent.MaxDepth = child.MaxDepth + 1 + } + return parent +} diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/testdata/diffs b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/testdata/diffs new file mode 100644 index 0000000..05118be --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/cmp/testdata/diffs @@ -0,0 +1,1706 @@ +<<< TestDiff/Comparer/StructInequal + struct{ A int; B int; C int }{ + A: 1, + B: 2, +- C: 3, ++ C: 4, + } +>>> TestDiff/Comparer/StructInequal +<<< TestDiff/Comparer/PointerStructInequal + &struct{ A *int }{ +- A: &4, ++ A: &5, + } +>>> TestDiff/Comparer/PointerStructInequal +<<< TestDiff/Comparer/StructNestedPointerInequal + &struct{ R *bytes.Buffer }{ +- R: s"", ++ R: nil, + } +>>> TestDiff/Comparer/StructNestedPointerInequal +<<< TestDiff/Comparer/RegexpInequal + []*regexp.Regexp{ + nil, +- s"a*b*c*", ++ s"a*b*d*", + } +>>> TestDiff/Comparer/RegexpInequal +<<< TestDiff/Comparer/TriplePointerInequal + &&&int( +- 0, ++ 1, + ) +>>> TestDiff/Comparer/TriplePointerInequal +<<< TestDiff/Comparer/StringerInequal + struct{ fmt.Stringer }( +- s"hello", ++ s"hello2", + ) +>>> TestDiff/Comparer/StringerInequal +<<< TestDiff/Comparer/DifferingHash + [16]uint8{ +- 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61, ++ 0x92, 0xeb, 0x5f, 0xfe, 0xe6, 0xae, 0x2f, 0xec, 0x3a, 0xd7, 0x1c, 0x77, 0x75, 0x31, 0x57, 0x8f, + } +>>> TestDiff/Comparer/DifferingHash +<<< TestDiff/Comparer/NilStringer + interface{}( +- &fmt.Stringer(nil), + ) +>>> TestDiff/Comparer/NilStringer +<<< TestDiff/Comparer/TarHeaders + []cmp_test.tarHeader{ + { + ... // 4 identical fields + Size: 1, + ModTime: s"2009-11-10 23:00:00 +0000 UTC", +- Typeflag: 48, ++ Typeflag: 0, + Linkname: "", + Uname: "user", + ... // 6 identical fields + }, + { + ... // 4 identical fields + Size: 2, + ModTime: s"2009-11-11 00:00:00 +0000 UTC", +- Typeflag: 48, ++ Typeflag: 0, + Linkname: "", + Uname: "user", + ... // 6 identical fields + }, + { + ... // 4 identical fields + Size: 4, + ModTime: s"2009-11-11 01:00:00 +0000 UTC", +- Typeflag: 48, ++ Typeflag: 0, + Linkname: "", + Uname: "user", + ... // 6 identical fields + }, + { + ... // 4 identical fields + Size: 8, + ModTime: s"2009-11-11 02:00:00 +0000 UTC", +- Typeflag: 48, ++ Typeflag: 0, + Linkname: "", + Uname: "user", + ... // 6 identical fields + }, + { + ... // 4 identical fields + Size: 16, + ModTime: s"2009-11-11 03:00:00 +0000 UTC", +- Typeflag: 48, ++ Typeflag: 0, + Linkname: "", + Uname: "user", + ... // 6 identical fields + }, + } +>>> TestDiff/Comparer/TarHeaders +<<< TestDiff/Comparer/IrreflexiveComparison + []int{ +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), +- Inverse(λ, float64(NaN)), ++ Inverse(λ, float64(NaN)), + } +>>> TestDiff/Comparer/IrreflexiveComparison +<<< TestDiff/Comparer/StringerMapKey + map[*testprotos.Stringer]*testprotos.Stringer( +- {s"hello": s"world"}, ++ nil, + ) +>>> TestDiff/Comparer/StringerMapKey +<<< TestDiff/Comparer/StringerBacktick + interface{}( +- []*testprotos.Stringer{s`multi\nline\nline\nline`}, + ) +>>> TestDiff/Comparer/StringerBacktick +<<< TestDiff/Comparer/DynamicMap + []interface{}{ + map[string]interface{}{ + "avg": float64(0.278), +- "hr": int(65), ++ "hr": float64(65), + "name": string("Mark McGwire"), + }, + map[string]interface{}{ + "avg": float64(0.288), +- "hr": int(63), ++ "hr": float64(63), + "name": string("Sammy Sosa"), + }, + } +>>> TestDiff/Comparer/DynamicMap +<<< TestDiff/Comparer/MapKeyPointer + map[*int]string{ +- &⟪0xdeadf00f⟫0: "hello", ++ &⟪0xdeadf00f⟫0: "world", + } +>>> TestDiff/Comparer/MapKeyPointer +<<< TestDiff/Comparer/IgnoreSliceElements + [2][]int{ + {..., 1, 2, 3, ...}, + { + ... // 6 ignored and 1 identical elements +- 20, ++ 2, + ... // 3 ignored elements + }, + } +>>> TestDiff/Comparer/IgnoreSliceElements +<<< TestDiff/Comparer/IgnoreMapEntries + [2]map[string]int{ + {"KEEP3": 3, "keep1": 1, "keep2": 2, ...}, + { + ... // 2 ignored entries + "keep1": 1, ++ "keep2": 2, + }, + } +>>> TestDiff/Comparer/IgnoreMapEntries +<<< TestDiff/Transformer/Uints + uint8(Inverse(λ, uint16(Inverse(λ, uint32(Inverse(λ, uint64( +- 0, ++ 1, + ))))))) +>>> TestDiff/Transformer/Uints +<<< TestDiff/Transformer/Filtered + []int{ + Inverse(λ, int64(0)), +- Inverse(λ, int64(-5)), ++ Inverse(λ, int64(3)), + Inverse(λ, int64(0)), +- Inverse(λ, int64(-1)), ++ Inverse(λ, int64(-5)), + } +>>> TestDiff/Transformer/Filtered +<<< TestDiff/Transformer/DisjointOutput + int(Inverse(λ, interface{}( +- string("zero"), ++ float64(1), + ))) +>>> TestDiff/Transformer/DisjointOutput +<<< TestDiff/Transformer/JSON + string(Inverse(ParseJSON, map[string]interface{}{ + "address": map[string]interface{}{ +- "city": string("Los Angeles"), ++ "city": string("New York"), + "postalCode": string("10021-3100"), +- "state": string("CA"), ++ "state": string("NY"), + "streetAddress": string("21 2nd Street"), + }, + "age": float64(25), + "children": []interface{}{}, + "firstName": string("John"), + "isAlive": bool(true), + "lastName": string("Smith"), + "phoneNumbers": []interface{}{ + map[string]interface{}{ +- "number": string("212 555-4321"), ++ "number": string("212 555-1234"), + "type": string("home"), + }, + map[string]interface{}{"number": string("646 555-4567"), "type": string("office")}, + map[string]interface{}{"number": string("123 456-7890"), "type": string("mobile")}, + }, ++ "spouse": nil, + })) +>>> TestDiff/Transformer/JSON +<<< TestDiff/Transformer/AcyclicString + cmp_test.StringBytes{ + String: Inverse(SplitString, []string{ + "some", + "multi", +- "Line", ++ "line", + "string", + }), + Bytes: []uint8(Inverse(SplitBytes, [][]uint8{ + {0x73, 0x6f, 0x6d, 0x65}, + {0x6d, 0x75, 0x6c, 0x74, ...}, + {0x6c, 0x69, 0x6e, 0x65}, + { +- 0x62, ++ 0x42, + 0x79, + 0x74, + ... // 2 identical elements + }, + })), + } +>>> TestDiff/Transformer/AcyclicString +<<< TestDiff/Reporter/PanicStringer + struct{ X fmt.Stringer }{ +- X: struct{ fmt.Stringer }{}, ++ X: s"", + } +>>> TestDiff/Reporter/PanicStringer +<<< TestDiff/Reporter/PanicError + struct{ X error }{ +- X: struct{ error }{}, ++ X: e"", + } +>>> TestDiff/Reporter/PanicError +<<< TestDiff/Reporter/AmbiguousType + interface{}( +- "github.com/google/go-cmp/cmp/internal/teststructs/foo1".Bar{}, ++ "github.com/google/go-cmp/cmp/internal/teststructs/foo2".Bar{}, + ) +>>> TestDiff/Reporter/AmbiguousType +<<< TestDiff/Reporter/AmbiguousPointer + (*int)( +- &⟪0xdeadf00f⟫0, ++ &⟪0xdeadf00f⟫0, + ) +>>> TestDiff/Reporter/AmbiguousPointer +<<< TestDiff/Reporter/AmbiguousPointerStruct + struct{ I *int }{ +- I: &⟪0xdeadf00f⟫0, ++ I: &⟪0xdeadf00f⟫0, + } +>>> TestDiff/Reporter/AmbiguousPointerStruct +<<< TestDiff/Reporter/AmbiguousPointerSlice + []*int{ +- &⟪0xdeadf00f⟫0, ++ &⟪0xdeadf00f⟫0, + } +>>> TestDiff/Reporter/AmbiguousPointerSlice +<<< TestDiff/Reporter/AmbiguousPointerMap + map[string]*int{ +- "zero": &⟪0xdeadf00f⟫0, ++ "zero": &⟪0xdeadf00f⟫0, + } +>>> TestDiff/Reporter/AmbiguousPointerMap +<<< TestDiff/Reporter/AmbiguousStringer + interface{}( +- cmp_test.Stringer("hello"), ++ &cmp_test.Stringer("hello"), + ) +>>> TestDiff/Reporter/AmbiguousStringer +<<< TestDiff/Reporter/AmbiguousStringerStruct + struct{ S fmt.Stringer }{ +- S: cmp_test.Stringer("hello"), ++ S: &cmp_test.Stringer("hello"), + } +>>> TestDiff/Reporter/AmbiguousStringerStruct +<<< TestDiff/Reporter/AmbiguousStringerSlice + []fmt.Stringer{ +- cmp_test.Stringer("hello"), ++ &cmp_test.Stringer("hello"), + } +>>> TestDiff/Reporter/AmbiguousStringerSlice +<<< TestDiff/Reporter/AmbiguousStringerMap + map[string]fmt.Stringer{ +- "zero": cmp_test.Stringer("hello"), ++ "zero": &cmp_test.Stringer("hello"), + } +>>> TestDiff/Reporter/AmbiguousStringerMap +<<< TestDiff/Reporter/AmbiguousSliceHeader + []int( +- ⟪ptr:0xdeadf00f, len:0, cap:5⟫{}, ++ ⟪ptr:0xdeadf00f, len:0, cap:1000⟫{}, + ) +>>> TestDiff/Reporter/AmbiguousSliceHeader +<<< TestDiff/Reporter/AmbiguousStringerMapKey + map[interface{}]string{ +- nil: "nil", ++ &⟪0xdeadf00f⟫"github.com/google/go-cmp/cmp_test".Stringer("hello"): "goodbye", +- "github.com/google/go-cmp/cmp_test".Stringer("hello"): "goodbye", +- "github.com/google/go-cmp/cmp/internal/teststructs/foo1".Bar{S: "fizz"}: "buzz", ++ "github.com/google/go-cmp/cmp/internal/teststructs/foo2".Bar{S: "fizz"}: "buzz", + } +>>> TestDiff/Reporter/AmbiguousStringerMapKey +<<< TestDiff/Reporter/NonAmbiguousStringerMapKey + map[interface{}]string{ ++ s"fizz": "buzz", +- s"hello": "goodbye", + } +>>> TestDiff/Reporter/NonAmbiguousStringerMapKey +<<< TestDiff/Reporter/InvalidUTF8 + interface{}( +- cmp_test.MyString("\xed\xa0\x80"), + ) +>>> TestDiff/Reporter/InvalidUTF8 +<<< TestDiff/Reporter/UnbatchedSlice + cmp_test.MyComposite{ + ... // 3 identical fields + BytesB: nil, + BytesC: nil, + IntsA: []int8{ ++ 10, + 11, +- 12, ++ 21, + 13, + 14, + ... // 15 identical elements + }, + IntsB: nil, + IntsC: nil, + ... // 6 identical fields + } +>>> TestDiff/Reporter/UnbatchedSlice +<<< TestDiff/Reporter/BatchedSlice + cmp_test.MyComposite{ + ... // 3 identical fields + BytesB: nil, + BytesC: nil, + IntsA: []int8{ +- 10, 11, 12, 13, 14, 15, 16, ++ 12, 29, 13, 27, 22, 23, + 17, 18, 19, 20, 21, +- 22, 23, 24, 25, 26, 27, 28, 29, ++ 10, 26, 16, 25, 28, 11, 15, 24, 14, + }, + IntsB: nil, + IntsC: nil, + ... // 6 identical fields + } +>>> TestDiff/Reporter/BatchedSlice +<<< TestDiff/Reporter/BatchedWithComparer + cmp_test.MyComposite{ + StringA: "", + StringB: "", + BytesA: []uint8{ +- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, // -|.......| ++ 0x0c, 0x1d, 0x0d, 0x1b, 0x16, 0x17, // +|......| + 0x11, 0x12, 0x13, 0x14, 0x15, // |.....| +- 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, // -|........| ++ 0x0a, 0x1a, 0x10, 0x19, 0x1c, 0x0b, 0x0f, 0x18, 0x0e, // +|.........| + }, + BytesB: nil, + BytesC: nil, + ... // 9 identical fields + } +>>> TestDiff/Reporter/BatchedWithComparer +<<< TestDiff/Reporter/BatchedLong + interface{}( +- cmp_test.MyComposite{IntsA: []int8{0, 1, 2, 3, 4, 5, 6, 7, ...}}, + ) +>>> TestDiff/Reporter/BatchedLong +<<< TestDiff/Reporter/BatchedNamedAndUnnamed + cmp_test.MyComposite{ + StringA: "", + StringB: "", + BytesA: []uint8{ +- 0x01, 0x02, 0x03, // -|...| ++ 0x03, 0x02, 0x01, // +|...| + }, + BytesB: []cmp_test.MyByte{ +- 0x04, 0x05, 0x06, ++ 0x06, 0x05, 0x04, + }, + BytesC: cmp_test.MyBytes{ +- 0x07, 0x08, 0x09, // -|...| ++ 0x09, 0x08, 0x07, // +|...| + }, + IntsA: []int8{ +- -1, -2, -3, ++ -3, -2, -1, + }, + IntsB: []cmp_test.MyInt{ +- -4, -5, -6, ++ -6, -5, -4, + }, + IntsC: cmp_test.MyInts{ +- -7, -8, -9, ++ -9, -8, -7, + }, + UintsA: []uint16{ +- 1000, 2000, 3000, ++ 3000, 2000, 1000, + }, + UintsB: []cmp_test.MyUint{ +- 4000, 5000, 6000, ++ 6000, 5000, 4000, + }, + UintsC: cmp_test.MyUints{ +- 7000, 8000, 9000, ++ 9000, 8000, 7000, + }, + FloatsA: []float32{ +- 1.5, 2.5, 3.5, ++ 3.5, 2.5, 1.5, + }, + FloatsB: []cmp_test.MyFloat{ +- 4.5, 5.5, 6.5, ++ 6.5, 5.5, 4.5, + }, + FloatsC: cmp_test.MyFloats{ +- 7.5, 8.5, 9.5, ++ 9.5, 8.5, 7.5, + }, + } +>>> TestDiff/Reporter/BatchedNamedAndUnnamed +<<< TestDiff/Reporter/BinaryHexdump + cmp_test.MyComposite{ + StringA: "", + StringB: "", + BytesA: []uint8{ + 0xf3, 0x0f, 0x8a, 0xa4, 0xd3, 0x12, 0x52, 0x09, 0x24, 0xbe, // |......R.$.| +- 0x58, 0x95, 0x41, 0xfd, 0x24, 0x66, 0x58, 0x8b, 0x79, // -|X.A.$fX.y| + 0x54, 0xac, 0x0d, 0xd8, 0x71, 0x77, 0x70, 0x20, 0x6a, 0x5c, 0x73, 0x7f, 0x8c, 0x17, 0x55, 0xc0, // |T...qwp j\s...U.| + 0x34, 0xce, 0x6e, 0xf7, 0xaa, 0x47, 0xee, 0x32, 0x9d, 0xc5, 0xca, 0x1e, 0x58, 0xaf, 0x8f, 0x27, // |4.n..G.2....X..'| + 0xf3, 0x02, 0x4a, 0x90, 0xed, 0x69, 0x2e, 0x70, 0x32, 0xb4, 0xab, 0x30, 0x20, 0xb6, 0xbd, 0x5c, // |..J..i.p2..0 ..\| + 0x62, 0x34, 0x17, 0xb0, 0x00, 0xbb, 0x4f, 0x7e, 0x27, 0x47, 0x06, 0xf4, 0x2e, 0x66, 0xfd, 0x63, // |b4....O~'G...f.c| + 0xd7, 0x04, 0xdd, 0xb7, 0x30, 0xb7, 0xd1, // |....0..| +- 0x55, 0x7e, 0x7b, 0xf6, 0xb3, 0x7e, 0x1d, 0x57, 0x69, // -|U~{..~.Wi| ++ 0x75, 0x2d, 0x5b, 0x5d, 0x5d, 0xf6, 0xb3, 0x68, 0x61, 0x68, 0x61, 0x7e, 0x1d, 0x57, 0x49, // +|u-[]]..haha~.WI| + 0x20, 0x9e, 0xbc, 0xdf, 0xe1, 0x4d, 0xa9, 0xef, 0xa2, 0xd2, 0xed, 0xb4, 0x47, 0x78, 0xc9, 0xc9, // | ....M......Gx..| + 0x27, 0xa4, 0xc6, 0xce, 0xec, 0x44, 0x70, 0x5d, // |'....Dp]| + }, + BytesB: nil, + BytesC: nil, + ... // 9 identical fields + } +>>> TestDiff/Reporter/BinaryHexdump +<<< TestDiff/Reporter/StringHexdump + cmp_test.MyComposite{ + StringA: "", + StringB: cmp_test.MyString{ +- 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, // -|readme| ++ 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, // +|gopher| + 0x2e, 0x74, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |.txt............| + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................| + ... // 64 identical bytes + 0x30, 0x30, 0x36, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, // |00600.0000000.00| + 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, // |00000.0000000004| +- 0x36, // -|6| ++ 0x33, // +|3| + 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x31, 0x31, // |.00000000000.011| +- 0x31, 0x37, 0x33, // -|173| ++ 0x32, 0x31, 0x37, // +|217| + 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |. 0.............| + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................| + ... // 326 identical bytes + }, + BytesA: nil, + BytesB: nil, + ... // 10 identical fields + } +>>> TestDiff/Reporter/StringHexdump +<<< TestDiff/Reporter/BinaryString + cmp_test.MyComposite{ + StringA: "", + StringB: "", + BytesA: bytes.Join({ + `{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"`, + `address":{"streetAddress":"`, +- "314 54th Avenue", ++ "21 2nd Street", + `","city":"New York","state":"NY","postalCode":"10021-3100"},"pho`, + `neNumbers":[{"type":"home","number":"212 555-1234"},{"type":"off`, + ... // 101 identical bytes + }, ""), + BytesB: nil, + BytesC: nil, + ... // 9 identical fields + } +>>> TestDiff/Reporter/BinaryString +<<< TestDiff/Reporter/TripleQuote + cmp_test.MyComposite{ + StringA: ( + """ + aaa + bbb +- ccc ++ CCC + ddd + eee + ... // 10 identical lines + ppp + qqq +- RRR +- sss ++ rrr ++ SSS + ttt + uuu + ... // 6 identical lines + """ + ), + StringB: "", + BytesA: nil, + ... // 11 identical fields + } +>>> TestDiff/Reporter/TripleQuote +<<< TestDiff/Reporter/TripleQuoteSlice + []string{ + ( + """ + ... // 23 identical lines + xxx + yyy +- zzz + """ + ), + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\n"..., + } +>>> TestDiff/Reporter/TripleQuoteSlice +<<< TestDiff/Reporter/TripleQuoteNamedTypes + cmp_test.MyComposite{ + StringA: "", + StringB: ( + """ + aaa + bbb +- ccc ++ CCC + ddd + eee + ... // 10 identical lines + ppp + qqq +- RRR +- sss ++ rrr ++ SSS + ttt + uuu + ... // 5 identical lines + """ + ), + BytesA: nil, + BytesB: nil, + BytesC: cmp_test.MyBytes( + """ + aaa + bbb +- ccc ++ CCC + ddd + eee + ... // 10 identical lines + ppp + qqq +- RRR +- sss ++ rrr ++ SSS + ttt + uuu + ... // 5 identical lines + """ + ), + IntsA: nil, + IntsB: nil, + ... // 7 identical fields + } +>>> TestDiff/Reporter/TripleQuoteNamedTypes +<<< TestDiff/Reporter/TripleQuoteSliceNamedTypes + []cmp_test.MyString{ + ( + """ + ... // 23 identical lines + xxx + yyy +- zzz + """ + ), + "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll\nmmm\nnnn\nooo\nppp\n"..., + } +>>> TestDiff/Reporter/TripleQuoteSliceNamedTypes +<<< TestDiff/Reporter/TripleQuoteEndlines + ( + """ + aaa + bbb +- ccc ++ CCC + ddd + eee + ... // 10 identical lines + ppp + qqq +- RRR ++ rrr + sss + ttt + ... // 4 identical lines + yyy + zzz +- + """ + ) +>>> TestDiff/Reporter/TripleQuoteEndlines +<<< TestDiff/Reporter/AvoidTripleQuoteAmbiguousQuotes + strings.Join({ + "aaa", + "bbb", +- "ccc", ++ "CCC", + "ddd", + "eee", +- "fff", ++ `"""`, + "ggg", + "hhh", + ... // 7 identical lines + "ppp", + "qqq", +- "RRR", ++ "rrr", + "sss", + "ttt", + ... // 7 identical lines + }, "\n") +>>> TestDiff/Reporter/AvoidTripleQuoteAmbiguousQuotes +<<< TestDiff/Reporter/AvoidTripleQuoteAmbiguousEllipsis + strings.Join({ + "aaa", + "bbb", +- "ccc", +- "...", ++ "CCC", ++ "ddd", + "eee", + "fff", + ... // 9 identical lines + "ppp", + "qqq", +- "RRR", ++ "rrr", + "sss", + "ttt", + ... // 7 identical lines + }, "\n") +>>> TestDiff/Reporter/AvoidTripleQuoteAmbiguousEllipsis +<<< TestDiff/Reporter/AvoidTripleQuoteNonPrintable + strings.Join({ + "aaa", + "bbb", +- "ccc", ++ "CCC", + "ddd", + "eee", + ... // 7 identical lines + "mmm", + "nnn", +- "ooo", ++ "o\roo", + "ppp", + "qqq", +- "RRR", ++ "rrr", + "sss", + "ttt", + ... // 7 identical lines + }, "\n") +>>> TestDiff/Reporter/AvoidTripleQuoteNonPrintable +<<< TestDiff/Reporter/AvoidTripleQuoteIdenticalWhitespace + strings.Join({ + "aaa", + "bbb", +- "ccc", +- " ddd", ++ "ccc ", ++ "ddd", + "eee", + "fff", + ... // 9 identical lines + "ppp", + "qqq", +- "RRR", ++ "rrr", + "sss", + "ttt", + ... // 7 identical lines + }, "\n") +>>> TestDiff/Reporter/AvoidTripleQuoteIdenticalWhitespace +<<< TestDiff/Reporter/TripleQuoteStringer + []fmt.Stringer{ + s"package main\n\nimport (\n\t\"fmt\"\n)\n\nfunc main() {\n\tfmt.Println(\"Hel"..., +- ( +- s""" +- package main +- +- import ( +- "fmt" +- "math/rand" +- ) +- +- func main() { +- fmt.Println("My favorite number is", rand.Intn(10)) +- } +- s""" +- ), ++ ( ++ s""" ++ package main ++ ++ import ( ++ "fmt" ++ "math" ++ ) ++ ++ func main() { ++ fmt.Printf("Now you have %g problems.\n", math.Sqrt(7)) ++ } ++ s""" ++ ), + } +>>> TestDiff/Reporter/TripleQuoteStringer +<<< TestDiff/Reporter/LimitMaximumBytesDiffs + []uint8{ +- 0xcd, 0x3d, 0x3d, 0x3d, 0x3d, 0x06, 0x1f, 0xc2, 0xcc, 0xc2, 0x2d, 0x53, // -|.====.....-S| ++ 0x5c, 0x3d, 0x3d, 0x3d, 0x3d, 0x7c, 0x96, 0xe7, 0x53, 0x42, 0xa0, 0xab, // +|\====|..SB..| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=====| +- 0x1d, 0xdf, 0x61, 0xae, 0x98, 0x9f, 0x48, // -|..a...H| ++ 0xf0, 0xbd, 0xa5, 0x71, 0xab, 0x17, 0x3b, // +|...q..;| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |======| +- 0xc7, 0xb0, 0xb7, // -|...| ++ 0xab, 0x50, 0x00, // +|.P.| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=======| +- 0xef, 0x3d, 0x3d, 0x3d, 0x3d, 0x3a, 0x5c, 0x94, 0xe6, 0x4a, 0xc7, // -|.====:\..J.| ++ 0xeb, 0x3d, 0x3d, 0x3d, 0x3d, 0xa5, 0x14, 0xe6, 0x4f, 0x28, 0xe4, // +|.====...O(.| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=====| +- 0xb4, // -|.| ++ 0x28, // +|(| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |======| +- 0x0a, 0x0a, 0xf7, 0x94, // -|....| ++ 0x2f, 0x63, 0x40, 0x3f, // +|/c@?| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |===========| +- 0xf2, 0x9c, 0xc0, 0x66, // -|...f| ++ 0xd9, 0x78, 0xed, 0x13, // +|.x..| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=====| +- 0x34, 0xf6, 0xf1, 0xc3, 0x17, 0x82, // -|4.....| ++ 0x4a, 0xfc, 0x91, 0x38, 0x42, 0x8d, // +|J..8B.| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |======| +- 0x6e, 0x16, 0x60, 0x91, 0x44, 0xc6, 0x06, // -|n.`.D..| ++ 0x61, 0x38, 0x41, 0xeb, 0x73, 0x04, 0xae, // +|a8A.s..| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=======| +- 0x1c, 0x45, 0x3d, 0x3d, 0x3d, 0x3d, 0x2e, // -|.E====.| ++ 0x07, 0x43, 0x3d, 0x3d, 0x3d, 0x3d, 0x1c, // +|.C====.| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |===========| +- 0xc4, 0x18, // -|..| ++ 0x91, 0x22, // +|."| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=======| +- 0x8a, 0x8d, 0x0e, 0x3d, 0x3d, 0x3d, 0x3d, 0x87, 0xb1, 0xa5, 0x8e, 0xc3, 0x3d, 0x3d, 0x3d, 0x3d, // -|...====.....====| +- 0x3d, 0x7a, 0x0f, 0x31, 0xae, 0x55, 0x3d, // -|=z.1.U=| ++ 0x75, 0xd8, 0xbe, 0x3d, 0x3d, 0x3d, 0x3d, 0x73, 0xec, 0x84, 0x35, 0x07, 0x3d, 0x3d, 0x3d, 0x3d, // +|u..====s..5.====| ++ 0x3d, 0x3b, 0xab, 0x53, 0x39, 0x74, // +|=;.S9t| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |=====| +- 0x47, 0x2c, 0x3d, // -|G,=| ++ 0x3d, 0x1f, 0x1b, // +|=..| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |======| +- 0x35, 0xe7, 0x35, 0xee, 0x82, 0xf4, 0xce, 0x3d, 0x3d, 0x3d, 0x3d, 0x11, 0x72, 0x3d, // -|5.5....====.r=| ++ 0x3d, 0x80, 0xab, 0x2f, 0xed, 0x2b, 0x3a, 0x3b, 0x3d, 0x3d, 0x3d, 0x3d, 0xea, 0x49, // +|=../.+:;====.I| + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, // |==========| +- 0xaf, 0x5d, 0x3d, // -|.]=| ++ 0x3d, 0xab, 0x6c, // +|=.l| + ... // 51 identical, 34 removed, and 35 inserted bytes + } +>>> TestDiff/Reporter/LimitMaximumBytesDiffs +<<< TestDiff/Reporter/LimitMaximumStringDiffs + ( + """ +- a ++ aa + b +- c ++ cc + d +- e ++ ee + f +- g ++ gg + h +- i ++ ii + j +- k ++ kk + l +- m ++ mm + n +- o ++ oo + p +- q ++ qq + r +- s ++ ss + t +- u ++ uu + v +- w ++ ww + x +- y ++ yy + z +- A ++ AA + B +- C ++ CC + D +- E ++ EE + ... // 12 identical, 10 removed, and 10 inserted lines + """ + ) +>>> TestDiff/Reporter/LimitMaximumStringDiffs +<<< TestDiff/Reporter/LimitMaximumSliceDiffs + []struct{ S string }{ +- {S: "a"}, ++ {S: "aa"}, + {S: "b"}, +- {S: "c"}, ++ {S: "cc"}, + {S: "d"}, +- {S: "e"}, ++ {S: "ee"}, + {S: "f"}, +- {S: "g"}, ++ {S: "gg"}, + {S: "h"}, +- {S: "i"}, ++ {S: "ii"}, + {S: "j"}, +- {S: "k"}, ++ {S: "kk"}, + {S: "l"}, +- {S: "m"}, ++ {S: "mm"}, + {S: "n"}, +- {S: "o"}, ++ {S: "oo"}, + {S: "p"}, +- {S: "q"}, ++ {S: "qq"}, + {S: "r"}, +- {S: "s"}, ++ {S: "ss"}, + {S: "t"}, +- {S: "u"}, ++ {S: "uu"}, + {S: "v"}, +- {S: "w"}, ++ {S: "ww"}, + {S: "x"}, +- {S: "y"}, ++ {S: "yy"}, + {S: "z"}, +- {S: "A"}, ++ {S: "AA"}, + {S: "B"}, +- {S: "C"}, ++ {S: "CC"}, + {S: "D"}, +- {S: "E"}, ++ {S: "EE"}, + ... // 12 identical and 10 modified elements + } +>>> TestDiff/Reporter/LimitMaximumSliceDiffs +<<< TestDiff/Reporter/MultilineString + cmp_test.MyComposite{ + StringA: ( + """ +- Package cmp determines equality of values. ++ Package cmp determines equality of value. + + This package is intended to be a more powerful and safer alternative to + ... // 6 identical lines + For example, an equality function may report floats as equal so long as they + are within some tolerance of each other. +- +- • Types that have an Equal method may use that method to determine equality. +- This allows package authors to determine the equality operation for the types +- that they define. + + • If no custom equality functions are used and no Equal method is defined, + ... // 3 identical lines + by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared + using the AllowUnexported option. +- + """ + ), + StringB: "", + BytesA: nil, + ... // 11 identical fields + } +>>> TestDiff/Reporter/MultilineString +<<< TestDiff/Reporter/Slices + cmp_test.MyComposite{ + StringA: "", + StringB: "", +- BytesA: []uint8{0x01, 0x02, 0x03}, ++ BytesA: nil, +- BytesB: []cmp_test.MyByte{0x04, 0x05, 0x06}, ++ BytesB: nil, +- BytesC: cmp_test.MyBytes{0x07, 0x08, 0x09}, ++ BytesC: nil, +- IntsA: []int8{-1, -2, -3}, ++ IntsA: nil, +- IntsB: []cmp_test.MyInt{-4, -5, -6}, ++ IntsB: nil, +- IntsC: cmp_test.MyInts{-7, -8, -9}, ++ IntsC: nil, +- UintsA: []uint16{1000, 2000, 3000}, ++ UintsA: nil, +- UintsB: []cmp_test.MyUint{4000, 5000, 6000}, ++ UintsB: nil, +- UintsC: cmp_test.MyUints{7000, 8000, 9000}, ++ UintsC: nil, +- FloatsA: []float32{1.5, 2.5, 3.5}, ++ FloatsA: nil, +- FloatsB: []cmp_test.MyFloat{4.5, 5.5, 6.5}, ++ FloatsB: nil, +- FloatsC: cmp_test.MyFloats{7.5, 8.5, 9.5}, ++ FloatsC: nil, + } +>>> TestDiff/Reporter/Slices +<<< TestDiff/Reporter/EmptySlices + cmp_test.MyComposite{ + StringA: "", + StringB: "", +- BytesA: []uint8{}, ++ BytesA: nil, +- BytesB: []cmp_test.MyByte{}, ++ BytesB: nil, +- BytesC: cmp_test.MyBytes{}, ++ BytesC: nil, +- IntsA: []int8{}, ++ IntsA: nil, +- IntsB: []cmp_test.MyInt{}, ++ IntsB: nil, +- IntsC: cmp_test.MyInts{}, ++ IntsC: nil, +- UintsA: []uint16{}, ++ UintsA: nil, +- UintsB: []cmp_test.MyUint{}, ++ UintsB: nil, +- UintsC: cmp_test.MyUints{}, ++ UintsC: nil, +- FloatsA: []float32{}, ++ FloatsA: nil, +- FloatsB: []cmp_test.MyFloat{}, ++ FloatsB: nil, +- FloatsC: cmp_test.MyFloats{}, ++ FloatsC: nil, + } +>>> TestDiff/Reporter/EmptySlices +<<< TestDiff/Reporter/LargeMapKey + map[*[]uint8]int{ +- &⟪0xdeadf00f⟫⟪ptr:0xdeadf00f, len:1048576, cap:1048576⟫{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ...}: 0, ++ &⟪0xdeadf00f⟫⟪ptr:0xdeadf00f, len:1048576, cap:1048576⟫{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ...}: 0, + } +>>> TestDiff/Reporter/LargeMapKey +<<< TestDiff/Reporter/LargeStringInInterface + struct{ X interface{} }{ + X: strings.Join({ + ... // 485 identical bytes + "s mus. Pellentesque mi lorem, consectetur id porttitor id, solli", + "citudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis", +- ".", ++ ",", + }, ""), + } +>>> TestDiff/Reporter/LargeStringInInterface +<<< TestDiff/Reporter/LargeBytesInInterface + struct{ X interface{} }{ + X: bytes.Join({ + ... // 485 identical bytes + "s mus. Pellentesque mi lorem, consectetur id porttitor id, solli", + "citudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis", +- ".", ++ ",", + }, ""), + } +>>> TestDiff/Reporter/LargeBytesInInterface +<<< TestDiff/Reporter/LargeStandaloneString + struct{ X interface{} }{ +- X: [1]string{ +- "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis.", +- }, ++ X: [1]string{ ++ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sit amet pretium ligula, at gravida quam. Integer iaculis, velit at sagittis ultricies, lacus metus scelerisque turpis, ornare feugiat nulla nisl ac erat. Maecenas elementum ultricies libero, sed efficitur lacus molestie non. Nulla ac pretium dolor. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque mi lorem, consectetur id porttitor id, sollicitudin sit amet enim. Duis eu dolor magna. Nunc ut augue turpis,", ++ }, + } +>>> TestDiff/Reporter/LargeStandaloneString +<<< TestDiff/EmbeddedStruct/ParentStructA/Inequal + teststructs.ParentStructA{ + privateStruct: teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructA/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructB/Inequal + teststructs.ParentStructB{ + PublicStruct: teststructs.PublicStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructB/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructC/Inequal + teststructs.ParentStructC{ + privateStruct: teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, +- Public: 3, ++ Public: 4, +- private: 4, ++ private: 5, + } +>>> TestDiff/EmbeddedStruct/ParentStructC/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructD/Inequal + teststructs.ParentStructD{ + PublicStruct: teststructs.PublicStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, +- Public: 3, ++ Public: 4, +- private: 4, ++ private: 5, + } +>>> TestDiff/EmbeddedStruct/ParentStructD/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructE/Inequal + teststructs.ParentStructE{ + privateStruct: teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + PublicStruct: teststructs.PublicStruct{ +- Public: 3, ++ Public: 4, +- private: 4, ++ private: 5, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructE/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructF/Inequal + teststructs.ParentStructF{ + privateStruct: teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + PublicStruct: teststructs.PublicStruct{ +- Public: 3, ++ Public: 4, +- private: 4, ++ private: 5, + }, +- Public: 5, ++ Public: 6, +- private: 6, ++ private: 7, + } +>>> TestDiff/EmbeddedStruct/ParentStructF/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructG/Inequal + &teststructs.ParentStructG{ + privateStruct: &teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructG/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructH/Inequal + &teststructs.ParentStructH{ + PublicStruct: &teststructs.PublicStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructH/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructI/Inequal + &teststructs.ParentStructI{ + privateStruct: &teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + PublicStruct: &teststructs.PublicStruct{ +- Public: 3, ++ Public: 4, +- private: 4, ++ private: 5, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructI/Inequal +<<< TestDiff/EmbeddedStruct/ParentStructJ/Inequal + &teststructs.ParentStructJ{ + privateStruct: &teststructs.privateStruct{ +- Public: 1, ++ Public: 2, +- private: 2, ++ private: 3, + }, + PublicStruct: &teststructs.PublicStruct{ +- Public: 3, ++ Public: 4, +- private: 4, ++ private: 5, + }, + Public: teststructs.PublicStruct{ +- Public: 7, ++ Public: 8, +- private: 8, ++ private: 9, + }, + private: teststructs.privateStruct{ +- Public: 5, ++ Public: 6, +- private: 6, ++ private: 7, + }, + } +>>> TestDiff/EmbeddedStruct/ParentStructJ/Inequal +<<< TestDiff/EqualMethod/StructB/ValueInequal + teststructs.StructB{ +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructB/ValueInequal +<<< TestDiff/EqualMethod/StructD/ValueInequal + teststructs.StructD{ +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructD/ValueInequal +<<< TestDiff/EqualMethod/StructE/ValueInequal + teststructs.StructE{ +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructE/ValueInequal +<<< TestDiff/EqualMethod/StructF/ValueInequal + teststructs.StructF{ +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructF/ValueInequal +<<< TestDiff/EqualMethod/StructA1/ValueInequal + teststructs.StructA1{ + StructA: {X: "NotEqual"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructA1/ValueInequal +<<< TestDiff/EqualMethod/StructA1/PointerInequal + &teststructs.StructA1{ + StructA: {X: "NotEqual"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructA1/PointerInequal +<<< TestDiff/EqualMethod/StructB1/ValueInequal + teststructs.StructB1{ + StructB: Inverse(Addr, &teststructs.StructB{X: "NotEqual"}), +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructB1/ValueInequal +<<< TestDiff/EqualMethod/StructB1/PointerInequal + &teststructs.StructB1{ + StructB: Inverse(Addr, &teststructs.StructB{X: "NotEqual"}), +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructB1/PointerInequal +<<< TestDiff/EqualMethod/StructD1/ValueInequal + teststructs.StructD1{ +- StructD: teststructs.StructD{X: "NotEqual"}, ++ StructD: teststructs.StructD{X: "not_equal"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructD1/ValueInequal +<<< TestDiff/EqualMethod/StructE1/ValueInequal + teststructs.StructE1{ +- StructE: teststructs.StructE{X: "NotEqual"}, ++ StructE: teststructs.StructE{X: "not_equal"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructE1/ValueInequal +<<< TestDiff/EqualMethod/StructF1/ValueInequal + teststructs.StructF1{ +- StructF: teststructs.StructF{X: "NotEqual"}, ++ StructF: teststructs.StructF{X: "not_equal"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructF1/ValueInequal +<<< TestDiff/EqualMethod/StructA2/ValueInequal + teststructs.StructA2{ + StructA: &{X: "NotEqual"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructA2/ValueInequal +<<< TestDiff/EqualMethod/StructA2/PointerInequal + &teststructs.StructA2{ + StructA: &{X: "NotEqual"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructA2/PointerInequal +<<< TestDiff/EqualMethod/StructB2/ValueInequal + teststructs.StructB2{ + StructB: &{X: "NotEqual"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructB2/ValueInequal +<<< TestDiff/EqualMethod/StructB2/PointerInequal + &teststructs.StructB2{ + StructB: &{X: "NotEqual"}, +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructB2/PointerInequal +<<< TestDiff/EqualMethod/StructNo/Inequal + teststructs.StructNo{ +- X: "NotEqual", ++ X: "not_equal", + } +>>> TestDiff/EqualMethod/StructNo/Inequal +<<< TestDiff/Cycle/PointersInequal + &&⟪ref#0⟫cmp_test.P( +- &⟪ref#0⟫(...), ++ &&⟪ref#0⟫(...), + ) +>>> TestDiff/Cycle/PointersInequal +<<< TestDiff/Cycle/SlicesInequal + cmp_test.S{ +- ⟪ref#0⟫{⟪ref#0⟫(...)}, ++ ⟪ref#1⟫{{⟪ref#1⟫(...)}}, + } +>>> TestDiff/Cycle/SlicesInequal +<<< TestDiff/Cycle/MapsInequal + cmp_test.M⟪ref#0⟫{ +- 0: ⟪ref#0⟫(...), ++ 0: {0: ⟪ref#0⟫(...)}, + } +>>> TestDiff/Cycle/MapsInequal +<<< TestDiff/Cycle/GraphInequalZeroed + map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫{ + Name: "Bar", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ +- ID: 102, ++ ID: 0, + Name: "BarBuzzBravo", + Mods: 2, + Alphas: map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫(...), + "Buzz": &⟪ref#2⟫{ + Name: "Buzz", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫(...), + "BuzzBarBravo": &⟪ref#3⟫{ +- ID: 103, ++ ID: 0, + Name: "BuzzBarBravo", + Mods: 0, + Alphas: {"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}, + }, + }, + }, + }, + }, + "BuzzBarBravo": &⟪ref#3⟫{ +- ID: 103, ++ ID: 0, + Name: "BuzzBarBravo", + Mods: 0, + Alphas: map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫(...), + "Buzz": &⟪ref#2⟫{ + Name: "Buzz", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ +- ID: 102, ++ ID: 0, + Name: "BarBuzzBravo", + Mods: 2, + Alphas: {"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}, + }, + "BuzzBarBravo": &⟪ref#3⟫(...), + }, + }, + }, + }, + }, + }, + "Buzz": &⟪ref#2⟫{ + Name: "Buzz", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ +- ID: 102, ++ ID: 0, + Name: "BarBuzzBravo", + Mods: 2, + Alphas: map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫{ + Name: "Bar", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫(...), + "BuzzBarBravo": &⟪ref#3⟫{ +- ID: 103, ++ ID: 0, + Name: "BuzzBarBravo", + Mods: 0, + Alphas: {"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}, + }, + }, + }, + "Buzz": &⟪ref#2⟫(...), + }, + }, + "BuzzBarBravo": &⟪ref#3⟫{ +- ID: 103, ++ ID: 0, + Name: "BuzzBarBravo", + Mods: 0, + Alphas: map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫{ + Name: "Bar", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ +- ID: 102, ++ ID: 0, + Name: "BarBuzzBravo", + Mods: 2, + Alphas: {"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}, + }, + "BuzzBarBravo": &⟪ref#3⟫(...), + }, + }, + "Buzz": &⟪ref#2⟫(...), + }, + }, + }, + }, + "Foo": &⟪ref#4⟫{ + Name: "Foo", + Bravos: map[string]*cmp_test.CycleBravo{ + "FooBravo": &{ +- ID: 101, ++ ID: 0, + Name: "FooBravo", + Mods: 100, + Alphas: {"Foo": &⟪ref#4⟫(...)}, + }, + }, + }, + } +>>> TestDiff/Cycle/GraphInequalZeroed +<<< TestDiff/Cycle/GraphInequalStruct + map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫{ + Name: "Bar", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ + ID: 102, + Name: "BarBuzzBravo", + Mods: 2, + Alphas: map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫(...), + "Buzz": &⟪ref#2⟫{ + Name: "Buzz", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫(...), + "BuzzBarBravo": &⟪ref#3⟫{ + ID: 103, + Name: "BuzzBarBravo", + Mods: 0, +- Alphas: nil, ++ Alphas: map[string]*cmp_test.CycleAlpha{"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}, + }, + }, + }, + }, + }, + "BuzzBarBravo": &⟪ref#3⟫{ + ID: 103, + Name: "BuzzBarBravo", + Mods: 0, + Alphas: map[string]*cmp_test.CycleAlpha{ + "Bar": &⟪ref#0⟫(...), + "Buzz": &⟪ref#2⟫{ + Name: "Buzz", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ID: 102, Name: "BarBuzzBravo", Mods: 2, Alphas: {"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}}, +- "BuzzBarBravo": &{ID: 103, Name: "BuzzBarBravo"}, ++ "BuzzBarBravo": &⟪ref#3⟫(...), + }, + }, + }, + }, + }, + }, + "Buzz": &⟪ref#2⟫{ + Name: "Buzz", + Bravos: map[string]*cmp_test.CycleBravo{ + "BarBuzzBravo": &⟪ref#1⟫{ID: 102, Name: "BarBuzzBravo", Mods: 2, Alphas: {"Bar": &⟪ref#0⟫{Name: "Bar", Bravos: {"BarBuzzBravo": &⟪ref#1⟫(...), "BuzzBarBravo": &⟪ref#3⟫{ID: 103, Name: "BuzzBarBravo", Alphas: {"Bar": &⟪ref#0⟫(...), "Buzz": &⟪ref#2⟫(...)}}}}, "Buzz": &⟪ref#2⟫(...)}}, + "BuzzBarBravo": &⟪ref#3⟫{ + ID: 103, + Name: "BuzzBarBravo", + Mods: 0, +- Alphas: nil, ++ Alphas: map[string]*cmp_test.CycleAlpha{ ++ "Bar": &⟪ref#0⟫{ ++ Name: "Bar", ++ Bravos: map[string]*cmp_test.CycleBravo{"BarBuzzBravo": &⟪ref#1⟫{...}, "BuzzBarBravo": &⟪ref#3⟫(...)}, ++ }, ++ "Buzz": &⟪ref#2⟫(...), ++ }, + }, + }, + }, + "Foo": &⟪ref#4⟫{Name: "Foo", Bravos: {"FooBravo": &{ID: 101, Name: "FooBravo", Mods: 100, Alphas: {"Foo": &⟪ref#4⟫(...)}}}}, + } +>>> TestDiff/Cycle/GraphInequalStruct +<<< TestDiff/Project1/ProtoInequal + teststructs.Eagle{ + ... // 4 identical fields + Dreamers: nil, + Prong: 0, + Slaps: []teststructs.Slap{ + ... // 2 identical elements + {}, + {}, + { + Name: "", + Desc: "", + DescLong: "", +- Args: s"metadata", ++ Args: s"metadata2", + Tense: 0, + Interval: 0, + ... // 3 identical fields + }, + }, + StateGoverner: "", + PrankRating: "", + ... // 2 identical fields + } +>>> TestDiff/Project1/ProtoInequal +<<< TestDiff/Project1/Inequal + teststructs.Eagle{ + ... // 2 identical fields + Desc: "some description", + DescLong: "", + Dreamers: []teststructs.Dreamer{ + {}, + { + ... // 4 identical fields + ContSlaps: nil, + ContSlapsInterval: 0, + Animal: []interface{}{ + teststructs.Goat{ + Target: "corporation", + Slaps: nil, + FunnyPrank: "", + Immutable: &teststructs.GoatImmutable{ +- ID: "southbay2", ++ ID: "southbay", +- State: &6, ++ State: &5, + Started: s"2009-11-10 23:00:00 +0000 UTC", + Stopped: s"0001-01-01 00:00:00 +0000 UTC", + ... // 1 ignored and 1 identical fields + }, + }, + teststructs.Donkey{}, + }, + Ornamental: false, + Amoeba: 53, + ... // 5 identical fields + }, + }, + Prong: 0, + Slaps: []teststructs.Slap{ + { + ... // 6 identical fields + Homeland: 0, + FunnyPrank: "", + Immutable: &teststructs.SlapImmutable{ + ID: "immutableSlap", + Out: nil, +- MildSlap: false, ++ MildSlap: true, + PrettyPrint: "", + State: nil, + Started: s"2009-11-10 23:00:00 +0000 UTC", + Stopped: s"0001-01-01 00:00:00 +0000 UTC", + LastUpdate: s"0001-01-01 00:00:00 +0000 UTC", + LoveRadius: &teststructs.LoveRadius{ + Summer: &teststructs.SummerLove{ + Summary: &teststructs.SummerLoveSummary{ + Devices: []string{ + "foo", +- "bar", +- "baz", + }, + ChangeType: {1, 2, 3}, + ... // 1 ignored field + }, + ... // 1 ignored field + }, + ... // 1 ignored field + }, + ... // 1 ignored field + }, + }, + }, + StateGoverner: "", + PrankRating: "", + ... // 2 identical fields + } +>>> TestDiff/Project1/Inequal +<<< TestDiff/Project2/InequalOrder + teststructs.GermBatch{ + DirtyGerms: map[int32][]*testprotos.Germ{ + 17: {s"germ1"}, + 18: { +- s"germ2", + s"germ3", + s"germ4", ++ s"germ2", + }, + }, + CleanGerms: nil, + GermMap: {13: s"germ13", 21: s"germ21"}, + ... // 7 identical fields + } +>>> TestDiff/Project2/InequalOrder +<<< TestDiff/Project2/Inequal + teststructs.GermBatch{ + DirtyGerms: map[int32][]*testprotos.Germ{ ++ 17: {s"germ1"}, + 18: Inverse(Sort, []*testprotos.Germ{ + s"germ2", + s"germ3", +- s"germ4", + }), + }, + CleanGerms: nil, + GermMap: {13: s"germ13", 21: s"germ21"}, + DishMap: map[int32]*teststructs.Dish{ + 0: &{err: e"EOF"}, +- 1: nil, ++ 1: &{err: e"unexpected EOF"}, + 2: &{pb: s"dish"}, + }, + HasPreviousResult: true, + DirtyID: 10, + CleanID: 0, +- GermStrain: 421, ++ GermStrain: 22, + TotalDirtyGerms: 0, + InfectedAt: s"2009-11-10 23:00:00 +0000 UTC", + } +>>> TestDiff/Project2/Inequal +<<< TestDiff/Project3/Inequal + teststructs.Dirt{ +- table: &teststructs.MockTable{state: []string{"a", "c"}}, ++ table: &teststructs.MockTable{state: []string{"a", "b", "c"}}, + ts: 12345, +- Discord: 554, ++ Discord: 500, +- Proto: testprotos.Dirt(Inverse(λ, s"blah")), ++ Proto: testprotos.Dirt(Inverse(λ, s"proto")), + wizard: map[string]*testprotos.Wizard{ +- "albus": s"dumbledore", +- "harry": s"potter", ++ "harry": s"otter", + }, + sadistic: nil, + lastTime: 54321, + ... // 1 ignored field + } +>>> TestDiff/Project3/Inequal +<<< TestDiff/Project4/Inequal + teststructs.Cartel{ + Headquarter: teststructs.Headquarter{ + id: 5, + location: "moon", + subDivisions: []string{ +- "alpha", + "bravo", + "charlie", + }, + incorporatedDate: s"0001-01-01 00:00:00 +0000 UTC", + metaData: s"metadata", + privateMessage: nil, + publicMessage: []uint8{ + 0x01, + 0x02, +- 0x03, ++ 0x04, +- 0x04, ++ 0x03, + 0x05, + }, + horseBack: "abcdef", + rattle: "", + ... // 5 identical fields + }, + source: "mars", + creationDate: s"0001-01-01 00:00:00 +0000 UTC", + boss: "al capone", + lastCrimeDate: s"0001-01-01 00:00:00 +0000 UTC", + poisons: []*teststructs.Poison{ + &{ +- poisonType: 1, ++ poisonType: 5, + expiration: s"2009-11-10 23:00:00 +0000 UTC", + manufacturer: "acme", + potency: 0, + }, +- &{poisonType: 2, manufacturer: "acme2"}, + }, + } +>>> TestDiff/Project4/Inequal diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.mod b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.mod new file mode 100644 index 0000000..5391dee --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.mod @@ -0,0 +1,5 @@ +module github.com/google/go-cmp + +go 1.8 + +require golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 diff --git a/root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.sum b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.sum new file mode 100644 index 0000000..3ab73ea --- /dev/null +++ b/root/pkg/mod/github.com/google/go-cmp@v0.5.5/go.sum @@ -0,0 +1,2 @@ +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/dependabot.yml b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/dependabot.yml new file mode 100644 index 0000000..6151c64 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "gomod" + directory: / + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/ci.yml b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/ci.yml new file mode 100644 index 0000000..952f872 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: [push] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + go: [ '1.20', '1.19', '1.18', '1.17', '1.16' ] + os: [ ubuntu-latest, macOS-latest, windows-latest ] + name: ${{ matrix.os }} Go ${{ matrix.go }} Tests + steps: + - uses: actions/checkout@v3 + - name: Setup go + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go }} + - run: go test diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/codeql-analysis.yml b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..db5c760 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/codeql-analysis.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '31 4 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/release.yml b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/release.yml new file mode 100644 index 0000000..e378b78 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.github/workflows/release.yml @@ -0,0 +1,31 @@ +on: + push: + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Upload Release Assets + +jobs: + build: + name: Upload Release Assets + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Generate build files + uses: thatisuday/go-cross-build@v1.0.2 + with: + platforms: 'linux/amd64, linux/ppc64le, darwin/amd64, darwin/arm64, windows/amd64' + package: 'cmd/godotenv' + name: 'godotenv' + compress: 'true' + dest: 'dist' + - name: Publish Binaries + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + release_name: Release ${{ github.ref }} + tag: ${{ github.ref }} + file: dist/* + file_glob: true + overwrite: true diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.gitignore b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/LICENCE b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/LICENCE new file mode 100644 index 0000000..e7ddd51 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/LICENCE @@ -0,0 +1,23 @@ +Copyright (c) 2013 John Barton + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/README.md b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/README.md new file mode 100644 index 0000000..bfbe66a --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/README.md @@ -0,0 +1,202 @@ +# GoDotEnv ![CI](https://github.com/joho/godotenv/workflows/CI/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/joho/godotenv)](https://goreportcard.com/report/github.com/joho/godotenv) + +A Go (golang) port of the Ruby [dotenv](https://github.com/bkeepers/dotenv) project (which loads env vars from a .env file). + +From the original Library: + +> Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables. +> +> But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped. + +It can be used as a library (for loading in env for your own daemons etc.) or as a bin command. + +There is test coverage and CI for both linuxish and Windows environments, but I make no guarantees about the bin version working on Windows. + +## Installation + +As a library + +```shell +go get github.com/joho/godotenv +``` + +or if you want to use it as a bin command + +go >= 1.17 +```shell +go install github.com/joho/godotenv/cmd/godotenv@latest +``` + +go < 1.17 +```shell +go get github.com/joho/godotenv/cmd/godotenv +``` + +## Usage + +Add your application configuration to your `.env` file in the root of your project: + +```shell +S3_BUCKET=YOURS3BUCKET +SECRET_KEY=YOURSECRETKEYGOESHERE +``` + +Then in your Go app you can do something like + +```go +package main + +import ( + "log" + "os" + + "github.com/joho/godotenv" +) + +func main() { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + s3Bucket := os.Getenv("S3_BUCKET") + secretKey := os.Getenv("SECRET_KEY") + + // now do something with s3 or whatever +} +``` + +If you're even lazier than that, you can just take advantage of the autoload package which will read in `.env` on import + +```go +import _ "github.com/joho/godotenv/autoload" +``` + +While `.env` in the project root is the default, you don't have to be constrained, both examples below are 100% legit + +```go +godotenv.Load("somerandomfile") +godotenv.Load("filenumberone.env", "filenumbertwo.env") +``` + +If you want to be really fancy with your env file you can do comments and exports (below is a valid env file) + +```shell +# I am a comment and that is OK +SOME_VAR=someval +FOO=BAR # comments at line end are OK too +export BAR=BAZ +``` + +Or finally you can do YAML(ish) style + +```yaml +FOO: bar +BAR: baz +``` + +as a final aside, if you don't want godotenv munging your env you can just get a map back instead + +```go +var myEnv map[string]string +myEnv, err := godotenv.Read() + +s3Bucket := myEnv["S3_BUCKET"] +``` + +... or from an `io.Reader` instead of a local file + +```go +reader := getRemoteFile() +myEnv, err := godotenv.Parse(reader) +``` + +... or from a `string` if you so desire + +```go +content := getRemoteFileContent() +myEnv, err := godotenv.Unmarshal(content) +``` + +### Precedence & Conventions + +Existing envs take precedence of envs that are loaded later. + +The [convention](https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use) +for managing multiple environments (i.e. development, test, production) +is to create an env named `{YOURAPP}_ENV` and load envs in this order: + +```go +env := os.Getenv("FOO_ENV") +if "" == env { + env = "development" +} + +godotenv.Load(".env." + env + ".local") +if "test" != env { + godotenv.Load(".env.local") +} +godotenv.Load(".env." + env) +godotenv.Load() // The Original .env +``` + +If you need to, you can also use `godotenv.Overload()` to defy this convention +and overwrite existing envs instead of only supplanting them. Use with caution. + +### Command Mode + +Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH` + +``` +godotenv -f /some/path/to/.env some_command with some args +``` + +If you don't specify `-f` it will fall back on the default of loading `.env` in `PWD` + +By default, it won't override existing environment variables; you can do that with the `-o` flag. + +### Writing Env Files + +Godotenv can also write a map representing the environment to a correctly-formatted and escaped file + +```go +env, err := godotenv.Unmarshal("KEY=value") +err := godotenv.Write(env, "./.env") +``` + +... or to a string + +```go +env, err := godotenv.Unmarshal("KEY=value") +content, err := godotenv.Marshal(env) +``` + +## Contributing + +Contributions are welcome, but with some caveats. + +This library has been declared feature complete (see [#182](https://github.com/joho/godotenv/issues/182) for background) and will not be accepting issues or pull requests adding new functionality or breaking the library API. + +Contributions would be gladly accepted that: + +* bring this library's parsing into closer compatibility with the mainline dotenv implementations, in particular [Ruby's dotenv](https://github.com/bkeepers/dotenv) and [Node.js' dotenv](https://github.com/motdotla/dotenv) +* keep the library up to date with the go ecosystem (ie CI bumps, documentation changes, changes in the core libraries) +* bug fixes for use cases that pertain to the library's purpose of easing development of codebases deployed into twelve factor environments + +*code changes without tests and references to peer dotenv implementations will not be accepted* + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Added some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request + +## Releases + +Releases should follow [Semver](http://semver.org/) though the first couple of releases are `v1` and `v1.1`. + +Use [annotated tags for all releases](https://github.com/joho/godotenv/issues/30). Example `git tag -a v1.2.1` + +## Who? + +The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](https://johnbarton.co/) based off the tests/fixtures in the original library. diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/autoload/autoload.go b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/autoload/autoload.go new file mode 100644 index 0000000..fbcd2bd --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/autoload/autoload.go @@ -0,0 +1,15 @@ +package autoload + +/* + You can just read the .env file on import just by doing + + import _ "github.com/joho/godotenv/autoload" + + And bob's your mother's brother +*/ + +import "github.com/joho/godotenv" + +func init() { + godotenv.Load() +} diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/cmd/godotenv/cmd.go b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/cmd/godotenv/cmd.go new file mode 100644 index 0000000..2a7b2d8 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/cmd/godotenv/cmd.go @@ -0,0 +1,56 @@ +package main + +import ( + "flag" + "fmt" + "log" + + "strings" + + "github.com/joho/godotenv" +) + +func main() { + var showHelp bool + flag.BoolVar(&showHelp, "h", false, "show help") + var rawEnvFilenames string + flag.StringVar(&rawEnvFilenames, "f", "", "comma separated paths to .env files") + var overload bool + flag.BoolVar(&overload, "o", false, "override existing .env variables") + + flag.Parse() + + usage := ` +Run a process with an env setup from a .env file + +godotenv [-o] [-f ENV_FILE_PATHS] COMMAND_ARGS + +ENV_FILE_PATHS: comma separated paths to .env files +COMMAND_ARGS: command and args you want to run + +example + godotenv -f /path/to/something/.env,/another/path/.env fortune +` + // if no args or -h flag + // print usage and return + args := flag.Args() + if showHelp || len(args) == 0 { + fmt.Println(usage) + return + } + + // load env + var envFilenames []string + if rawEnvFilenames != "" { + envFilenames = strings.Split(rawEnvFilenames, ",") + } + + // take rest of args and "exec" them + cmd := args[0] + cmdArgs := args[1:] + + err := godotenv.Exec(envFilenames, cmd, cmdArgs, overload) + if err != nil { + log.Fatal(err) + } +} diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/comments.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/comments.env new file mode 100644 index 0000000..af9781f --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/comments.env @@ -0,0 +1,4 @@ +# Full line comment +foo=bar # baz +bar=foo#baz +baz="foo"#bar diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/equals.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/equals.env new file mode 100644 index 0000000..00c9809 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/equals.env @@ -0,0 +1 @@ +export OPTION_A='postgres://localhost:5432/database?sslmode=disable' diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/exported.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/exported.env new file mode 100644 index 0000000..5821377 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/exported.env @@ -0,0 +1,2 @@ +export OPTION_A=2 +export OPTION_B='\n' diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/invalid1.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/invalid1.env new file mode 100644 index 0000000..38f7e0e --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/invalid1.env @@ -0,0 +1,2 @@ +INVALID LINE +foo=bar diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/plain.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/plain.env new file mode 100644 index 0000000..e033366 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/plain.env @@ -0,0 +1,8 @@ +OPTION_A=1 +OPTION_B=2 +OPTION_C= 3 +OPTION_D =4 +OPTION_E = 5 +OPTION_F = +OPTION_G= +OPTION_H=1 2 \ No newline at end of file diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/quoted.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/quoted.env new file mode 100644 index 0000000..7958933 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/quoted.env @@ -0,0 +1,19 @@ +OPTION_A='1' +OPTION_B='2' +OPTION_C='' +OPTION_D='\n' +OPTION_E="1" +OPTION_F="2" +OPTION_G="" +OPTION_H="\n" +OPTION_I = "echo 'asd'" +OPTION_J='line 1 +line 2' +OPTION_K='line one +this is \'quoted\' +one more line' +OPTION_L="line 1 +line 2" +OPTION_M="line one +this is \"quoted\" +one more line" diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/substitutions.env b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/substitutions.env new file mode 100644 index 0000000..44337a9 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/fixtures/substitutions.env @@ -0,0 +1,5 @@ +OPTION_A=1 +OPTION_B=${OPTION_A} +OPTION_C=$OPTION_B +OPTION_D=${OPTION_A}${OPTION_B} +OPTION_E=${OPTION_NOT_DEFINED} diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/go.mod b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/go.mod new file mode 100644 index 0000000..126e61d --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/go.mod @@ -0,0 +1,3 @@ +module github.com/joho/godotenv + +go 1.12 diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv.go b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv.go new file mode 100644 index 0000000..61b0ebb --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv.go @@ -0,0 +1,228 @@ +// Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv) +// +// Examples/readme can be found on the GitHub page at https://github.com/joho/godotenv +// +// The TL;DR is that you make a .env file that looks something like +// +// SOME_ENV_VAR=somevalue +// +// and then in your go code you can call +// +// godotenv.Load() +// +// and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR") +package godotenv + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "sort" + "strconv" + "strings" +) + +const doubleQuoteSpecialChars = "\\\n\r\"!$`" + +// Parse reads an env file from io.Reader, returning a map of keys and values. +func Parse(r io.Reader) (map[string]string, error) { + var buf bytes.Buffer + _, err := io.Copy(&buf, r) + if err != nil { + return nil, err + } + + return UnmarshalBytes(buf.Bytes()) +} + +// Load will read your env file(s) and load them into ENV for this process. +// +// Call this function as close as possible to the start of your program (ideally in main). +// +// If you call Load without any args it will default to loading .env in the current path. +// +// You can otherwise tell it which files to load (there can be more than one) like: +// +// godotenv.Load("fileone", "filetwo") +// +// It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults. +func Load(filenames ...string) (err error) { + filenames = filenamesOrDefault(filenames) + + for _, filename := range filenames { + err = loadFile(filename, false) + if err != nil { + return // return early on a spazout + } + } + return +} + +// Overload will read your env file(s) and load them into ENV for this process. +// +// Call this function as close as possible to the start of your program (ideally in main). +// +// If you call Overload without any args it will default to loading .env in the current path. +// +// You can otherwise tell it which files to load (there can be more than one) like: +// +// godotenv.Overload("fileone", "filetwo") +// +// It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefully set all vars. +func Overload(filenames ...string) (err error) { + filenames = filenamesOrDefault(filenames) + + for _, filename := range filenames { + err = loadFile(filename, true) + if err != nil { + return // return early on a spazout + } + } + return +} + +// Read all env (with same file loading semantics as Load) but return values as +// a map rather than automatically writing values into env +func Read(filenames ...string) (envMap map[string]string, err error) { + filenames = filenamesOrDefault(filenames) + envMap = make(map[string]string) + + for _, filename := range filenames { + individualEnvMap, individualErr := readFile(filename) + + if individualErr != nil { + err = individualErr + return // return early on a spazout + } + + for key, value := range individualEnvMap { + envMap[key] = value + } + } + + return +} + +// Unmarshal reads an env file from a string, returning a map of keys and values. +func Unmarshal(str string) (envMap map[string]string, err error) { + return UnmarshalBytes([]byte(str)) +} + +// UnmarshalBytes parses env file from byte slice of chars, returning a map of keys and values. +func UnmarshalBytes(src []byte) (map[string]string, error) { + out := make(map[string]string) + err := parseBytes(src, out) + + return out, err +} + +// Exec loads env vars from the specified filenames (empty map falls back to default) +// then executes the cmd specified. +// +// Simply hooks up os.Stdin/err/out to the command and calls Run(). +// +// If you want more fine grained control over your command it's recommended +// that you use `Load()`, `Overload()` or `Read()` and the `os/exec` package yourself. +func Exec(filenames []string, cmd string, cmdArgs []string, overload bool) error { + op := Load + if overload { + op = Overload + } + if err := op(filenames...); err != nil { + return err + } + + command := exec.Command(cmd, cmdArgs...) + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + return command.Run() +} + +// Write serializes the given environment and writes it to a file. +func Write(envMap map[string]string, filename string) error { + content, err := Marshal(envMap) + if err != nil { + return err + } + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + _, err = file.WriteString(content + "\n") + if err != nil { + return err + } + return file.Sync() +} + +// Marshal outputs the given environment as a dotenv-formatted environment file. +// Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. +func Marshal(envMap map[string]string) (string, error) { + lines := make([]string, 0, len(envMap)) + for k, v := range envMap { + if d, err := strconv.Atoi(v); err == nil { + lines = append(lines, fmt.Sprintf(`%s=%d`, k, d)) + } else { + lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v))) + } + } + sort.Strings(lines) + return strings.Join(lines, "\n"), nil +} + +func filenamesOrDefault(filenames []string) []string { + if len(filenames) == 0 { + return []string{".env"} + } + return filenames +} + +func loadFile(filename string, overload bool) error { + envMap, err := readFile(filename) + if err != nil { + return err + } + + currentEnv := map[string]bool{} + rawEnv := os.Environ() + for _, rawEnvLine := range rawEnv { + key := strings.Split(rawEnvLine, "=")[0] + currentEnv[key] = true + } + + for key, value := range envMap { + if !currentEnv[key] || overload { + _ = os.Setenv(key, value) + } + } + + return nil +} + +func readFile(filename string) (envMap map[string]string, err error) { + file, err := os.Open(filename) + if err != nil { + return + } + defer file.Close() + + return Parse(file) +} + +func doubleQuoteEscape(line string) string { + for _, c := range doubleQuoteSpecialChars { + toReplace := "\\" + string(c) + if c == '\n' { + toReplace = `\n` + } + if c == '\r' { + toReplace = `\r` + } + line = strings.Replace(line, string(c), toReplace, -1) + } + return line +} diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv_test.go b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv_test.go new file mode 100644 index 0000000..edb5781 --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/godotenv_test.go @@ -0,0 +1,575 @@ +package godotenv + +import ( + "bytes" + "fmt" + "os" + "reflect" + "strings" + "testing" +) + +var noopPresets = make(map[string]string) + +func parseAndCompare(t *testing.T, rawEnvLine string, expectedKey string, expectedValue string) { + result, err := Unmarshal(rawEnvLine) + + if err != nil { + t.Errorf("Expected %q to parse as %q: %q, errored %q", rawEnvLine, expectedKey, expectedValue, err) + return + } + if result[expectedKey] != expectedValue { + t.Errorf("Expected '%v' to parse as '%v' => '%v', got %q instead", rawEnvLine, expectedKey, expectedValue, result) + } +} + +func loadEnvAndCompareValues(t *testing.T, loader func(files ...string) error, envFileName string, expectedValues map[string]string, presets map[string]string) { + // first up, clear the env + os.Clearenv() + + for k, v := range presets { + os.Setenv(k, v) + } + + err := loader(envFileName) + if err != nil { + t.Fatalf("Error loading %v", envFileName) + } + + for k := range expectedValues { + envValue := os.Getenv(k) + v := expectedValues[k] + if envValue != v { + t.Errorf("Mismatch for key '%v': expected '%#v' got '%#v'", k, v, envValue) + } + } +} + +func TestLoadWithNoArgsLoadsDotEnv(t *testing.T) { + err := Load() + pathError := err.(*os.PathError) + if pathError == nil || pathError.Op != "open" || pathError.Path != ".env" { + t.Errorf("Didn't try and open .env by default") + } +} + +func TestOverloadWithNoArgsOverloadsDotEnv(t *testing.T) { + err := Overload() + pathError := err.(*os.PathError) + if pathError == nil || pathError.Op != "open" || pathError.Path != ".env" { + t.Errorf("Didn't try and open .env by default") + } +} + +func TestLoadFileNotFound(t *testing.T) { + err := Load("somefilethatwillneverexistever.env") + if err == nil { + t.Error("File wasn't found but Load didn't return an error") + } +} + +func TestOverloadFileNotFound(t *testing.T) { + err := Overload("somefilethatwillneverexistever.env") + if err == nil { + t.Error("File wasn't found but Overload didn't return an error") + } +} + +func TestReadPlainEnv(t *testing.T) { + envFileName := "fixtures/plain.env" + expectedValues := map[string]string{ + "OPTION_A": "1", + "OPTION_B": "2", + "OPTION_C": "3", + "OPTION_D": "4", + "OPTION_E": "5", + "OPTION_F": "", + "OPTION_G": "", + "OPTION_H": "1 2", + } + + envMap, err := Read(envFileName) + if err != nil { + t.Error("Error reading file") + } + + if len(envMap) != len(expectedValues) { + t.Error("Didn't get the right size map back") + } + + for key, value := range expectedValues { + if envMap[key] != value { + t.Error("Read got one of the keys wrong") + } + } +} + +func TestParse(t *testing.T) { + envMap, err := Parse(bytes.NewReader([]byte("ONE=1\nTWO='2'\nTHREE = \"3\""))) + expectedValues := map[string]string{ + "ONE": "1", + "TWO": "2", + "THREE": "3", + } + if err != nil { + t.Fatalf("error parsing env: %v", err) + } + for key, value := range expectedValues { + if envMap[key] != value { + t.Errorf("expected %s to be %s, got %s", key, value, envMap[key]) + } + } +} + +func TestLoadDoesNotOverride(t *testing.T) { + envFileName := "fixtures/plain.env" + + // ensure NO overload + presets := map[string]string{ + "OPTION_A": "do_not_override", + "OPTION_B": "", + } + + expectedValues := map[string]string{ + "OPTION_A": "do_not_override", + "OPTION_B": "", + } + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, presets) +} + +func TestOverloadDoesOverride(t *testing.T) { + envFileName := "fixtures/plain.env" + + // ensure NO overload + presets := map[string]string{ + "OPTION_A": "do_not_override", + } + + expectedValues := map[string]string{ + "OPTION_A": "1", + } + loadEnvAndCompareValues(t, Overload, envFileName, expectedValues, presets) +} + +func TestLoadPlainEnv(t *testing.T) { + envFileName := "fixtures/plain.env" + expectedValues := map[string]string{ + "OPTION_A": "1", + "OPTION_B": "2", + "OPTION_C": "3", + "OPTION_D": "4", + "OPTION_E": "5", + "OPTION_H": "1 2", + } + + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) +} + +func TestLoadExportedEnv(t *testing.T) { + envFileName := "fixtures/exported.env" + expectedValues := map[string]string{ + "OPTION_A": "2", + "OPTION_B": "\\n", + } + + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) +} + +func TestLoadEqualsEnv(t *testing.T) { + envFileName := "fixtures/equals.env" + expectedValues := map[string]string{ + "OPTION_A": "postgres://localhost:5432/database?sslmode=disable", + } + + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) +} + +func TestLoadQuotedEnv(t *testing.T) { + envFileName := "fixtures/quoted.env" + expectedValues := map[string]string{ + "OPTION_A": "1", + "OPTION_B": "2", + "OPTION_C": "", + "OPTION_D": "\\n", + "OPTION_E": "1", + "OPTION_F": "2", + "OPTION_G": "", + "OPTION_H": "\n", + "OPTION_I": "echo 'asd'", + "OPTION_J": "line 1\nline 2", + "OPTION_K": "line one\nthis is \\'quoted\\'\none more line", + "OPTION_L": "line 1\nline 2", + "OPTION_M": "line one\nthis is \"quoted\"\none more line", + } + + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) +} + +func TestSubstitutions(t *testing.T) { + envFileName := "fixtures/substitutions.env" + expectedValues := map[string]string{ + "OPTION_A": "1", + "OPTION_B": "1", + "OPTION_C": "1", + "OPTION_D": "11", + "OPTION_E": "", + } + + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) +} + +func TestExpanding(t *testing.T) { + tests := []struct { + name string + input string + expected map[string]string + }{ + { + "expands variables found in values", + "FOO=test\nBAR=$FOO", + map[string]string{"FOO": "test", "BAR": "test"}, + }, + { + "parses variables wrapped in brackets", + "FOO=test\nBAR=${FOO}bar", + map[string]string{"FOO": "test", "BAR": "testbar"}, + }, + { + "expands undefined variables to an empty string", + "BAR=$FOO", + map[string]string{"BAR": ""}, + }, + { + "expands variables in double quoted strings", + "FOO=test\nBAR=\"quote $FOO\"", + map[string]string{"FOO": "test", "BAR": "quote test"}, + }, + { + "does not expand variables in single quoted strings", + "BAR='quote $FOO'", + map[string]string{"BAR": "quote $FOO"}, + }, + { + "does not expand escaped variables", + `FOO="foo\$BAR"`, + map[string]string{"FOO": "foo$BAR"}, + }, + { + "does not expand escaped variables", + `FOO="foo\${BAR}"`, + map[string]string{"FOO": "foo${BAR}"}, + }, + { + "does not expand escaped variables", + "FOO=test\nBAR=\"foo\\${FOO} ${FOO}\"", + map[string]string{"FOO": "test", "BAR": "foo${FOO} test"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + env, err := Parse(strings.NewReader(tt.input)) + if err != nil { + t.Errorf("Error: %s", err.Error()) + } + for k, v := range tt.expected { + if strings.Compare(env[k], v) != 0 { + t.Errorf("Expected: %s, Actual: %s", v, env[k]) + } + } + }) + } +} + +func TestVariableStringValueSeparator(t *testing.T) { + input := "TEST_URLS=\"stratum+tcp://stratum.antpool.com:3333\nstratum+tcp://stratum.antpool.com:443\"" + want := map[string]string{ + "TEST_URLS": "stratum+tcp://stratum.antpool.com:3333\nstratum+tcp://stratum.antpool.com:443", + } + got, err := Parse(strings.NewReader(input)) + if err != nil { + t.Error(err) + } + + if len(got) != len(want) { + t.Fatalf( + "unexpected value:\nwant:\n\t%#v\n\ngot:\n\t%#v", want, got) + } + + for k, wantVal := range want { + gotVal, ok := got[k] + if !ok { + t.Fatalf("key %q doesn't present in result", k) + } + if wantVal != gotVal { + t.Fatalf( + "mismatch in %q value:\nwant:\n\t%s\n\ngot:\n\t%s", k, + wantVal, gotVal) + } + } +} + +func TestActualEnvVarsAreLeftAlone(t *testing.T) { + os.Clearenv() + os.Setenv("OPTION_A", "actualenv") + _ = Load("fixtures/plain.env") + + if os.Getenv("OPTION_A") != "actualenv" { + t.Error("An ENV var set earlier was overwritten") + } +} + +func TestParsing(t *testing.T) { + // unquoted values + parseAndCompare(t, "FOO=bar", "FOO", "bar") + + // parses values with spaces around equal sign + parseAndCompare(t, "FOO =bar", "FOO", "bar") + parseAndCompare(t, "FOO= bar", "FOO", "bar") + + // parses double quoted values + parseAndCompare(t, `FOO="bar"`, "FOO", "bar") + + // parses single quoted values + parseAndCompare(t, "FOO='bar'", "FOO", "bar") + + // parses escaped double quotes + parseAndCompare(t, `FOO="escaped\"bar"`, "FOO", `escaped"bar`) + + // parses single quotes inside double quotes + parseAndCompare(t, `FOO="'d'"`, "FOO", `'d'`) + + // parses yaml style options + parseAndCompare(t, "OPTION_A: 1", "OPTION_A", "1") + + //parses yaml values with equal signs + parseAndCompare(t, "OPTION_A: Foo=bar", "OPTION_A", "Foo=bar") + + // parses non-yaml options with colons + parseAndCompare(t, "OPTION_A=1:B", "OPTION_A", "1:B") + + // parses export keyword + parseAndCompare(t, "export OPTION_A=2", "OPTION_A", "2") + parseAndCompare(t, `export OPTION_B='\n'`, "OPTION_B", "\\n") + parseAndCompare(t, "export exportFoo=2", "exportFoo", "2") + parseAndCompare(t, "exportFOO=2", "exportFOO", "2") + parseAndCompare(t, "export_FOO =2", "export_FOO", "2") + parseAndCompare(t, "export.FOO= 2", "export.FOO", "2") + parseAndCompare(t, "export\tOPTION_A=2", "OPTION_A", "2") + parseAndCompare(t, " export OPTION_A=2", "OPTION_A", "2") + parseAndCompare(t, "\texport OPTION_A=2", "OPTION_A", "2") + + // it 'expands newlines in quoted strings' do + // expect(env('FOO="bar\nbaz"')).to eql('FOO' => "bar\nbaz") + parseAndCompare(t, `FOO="bar\nbaz"`, "FOO", "bar\nbaz") + + // it 'parses variables with "." in the name' do + // expect(env('FOO.BAR=foobar')).to eql('FOO.BAR' => 'foobar') + parseAndCompare(t, "FOO.BAR=foobar", "FOO.BAR", "foobar") + + // it 'parses variables with several "=" in the value' do + // expect(env('FOO=foobar=')).to eql('FOO' => 'foobar=') + parseAndCompare(t, "FOO=foobar=", "FOO", "foobar=") + + // it 'strips unquoted values' do + // expect(env('foo=bar ')).to eql('foo' => 'bar') # not 'bar ' + parseAndCompare(t, "FOO=bar ", "FOO", "bar") + + // unquoted internal whitespace is preserved + parseAndCompare(t, `KEY=value value`, "KEY", "value value") + + // it 'ignores inline comments' do + // expect(env("foo=bar # this is foo")).to eql('foo' => 'bar') + parseAndCompare(t, "FOO=bar # this is foo", "FOO", "bar") + + // it 'allows # in quoted value' do + // expect(env('foo="bar#baz" # comment')).to eql('foo' => 'bar#baz') + parseAndCompare(t, `FOO="bar#baz" # comment`, "FOO", "bar#baz") + parseAndCompare(t, "FOO='bar#baz' # comment", "FOO", "bar#baz") + parseAndCompare(t, `FOO="bar#baz#bang" # comment`, "FOO", "bar#baz#bang") + + // it 'parses # in quoted values' do + // expect(env('foo="ba#r"')).to eql('foo' => 'ba#r') + // expect(env("foo='ba#r'")).to eql('foo' => 'ba#r') + parseAndCompare(t, `FOO="ba#r"`, "FOO", "ba#r") + parseAndCompare(t, "FOO='ba#r'", "FOO", "ba#r") + + //newlines and backslashes should be escaped + parseAndCompare(t, `FOO="bar\n\ b\az"`, "FOO", "bar\n baz") + parseAndCompare(t, `FOO="bar\\\n\ b\az"`, "FOO", "bar\\\n baz") + parseAndCompare(t, `FOO="bar\\r\ b\az"`, "FOO", "bar\\r baz") + + parseAndCompare(t, `="value"`, "", "value") + + // unquoted whitespace around keys should be ignored + parseAndCompare(t, " KEY =value", "KEY", "value") + parseAndCompare(t, " KEY=value", "KEY", "value") + parseAndCompare(t, "\tKEY=value", "KEY", "value") + + // it 'throws an error if line format is incorrect' do + // expect{env('lol$wut')}.to raise_error(Dotenv::FormatError) + badlyFormattedLine := "lol$wut" + _, err := Unmarshal(badlyFormattedLine) + if err == nil { + t.Errorf("Expected \"%v\" to return error, but it didn't", badlyFormattedLine) + } +} + +func TestLinesToIgnore(t *testing.T) { + cases := map[string]struct { + input string + want string + }{ + "Line with nothing but line break": { + input: "\n", + }, + "Line with nothing but windows-style line break": { + input: "\r\n", + }, + "Line full of whitespace": { + input: "\t\t ", + }, + "Comment": { + input: "# Comment", + }, + "Indented comment": { + input: "\t # comment", + }, + "non-ignored value": { + input: `export OPTION_B='\n'`, + want: `export OPTION_B='\n'`, + }, + } + + for n, c := range cases { + t.Run(n, func(t *testing.T) { + got := string(getStatementStart([]byte(c.input))) + if got != c.want { + t.Errorf("Expected:\t %q\nGot:\t %q", c.want, got) + } + }) + } +} + +func TestErrorReadDirectory(t *testing.T) { + envFileName := "fixtures/" + envMap, err := Read(envFileName) + + if err == nil { + t.Errorf("Expected error, got %v", envMap) + } +} + +func TestErrorParsing(t *testing.T) { + envFileName := "fixtures/invalid1.env" + envMap, err := Read(envFileName) + if err == nil { + t.Errorf("Expected error, got %v", envMap) + } +} + +func TestComments(t *testing.T) { + envFileName := "fixtures/comments.env" + expectedValues := map[string]string{ + "foo": "bar", + "bar": "foo#baz", + "baz": "foo", + } + + loadEnvAndCompareValues(t, Load, envFileName, expectedValues, noopPresets) +} + +func TestWrite(t *testing.T) { + writeAndCompare := func(env string, expected string) { + envMap, _ := Unmarshal(env) + actual, _ := Marshal(envMap) + if expected != actual { + t.Errorf("Expected '%v' (%v) to write as '%v', got '%v' instead.", env, envMap, expected, actual) + } + } + //just test some single lines to show the general idea + //TestRoundtrip makes most of the good assertions + + //values are always double-quoted + writeAndCompare(`key=value`, `key="value"`) + //double-quotes are escaped + writeAndCompare(`key=va"lu"e`, `key="va\"lu\"e"`) + //but single quotes are left alone + writeAndCompare(`key=va'lu'e`, `key="va'lu'e"`) + // newlines, backslashes, and some other special chars are escaped + writeAndCompare(`foo="\n\r\\r!"`, `foo="\n\r\\r\!"`) + // lines should be sorted + writeAndCompare("foo=bar\nbaz=buzz", "baz=\"buzz\"\nfoo=\"bar\"") + // integers should not be quoted + writeAndCompare(`key="10"`, `key=10`) + +} + +func TestRoundtrip(t *testing.T) { + fixtures := []string{"equals.env", "exported.env", "plain.env", "quoted.env"} + for _, fixture := range fixtures { + fixtureFilename := fmt.Sprintf("fixtures/%s", fixture) + env, err := readFile(fixtureFilename) + if err != nil { + t.Errorf("Expected '%s' to read without error (%v)", fixtureFilename, err) + } + rep, err := Marshal(env) + if err != nil { + t.Errorf("Expected '%s' to Marshal (%v)", fixtureFilename, err) + } + roundtripped, err := Unmarshal(rep) + if err != nil { + t.Errorf("Expected '%s' to Mashal and Unmarshal (%v)", fixtureFilename, err) + } + if !reflect.DeepEqual(env, roundtripped) { + t.Errorf("Expected '%s' to roundtrip as '%v', got '%v' instead", fixtureFilename, env, roundtripped) + } + + } +} + +func TestTrailingNewlines(t *testing.T) { + cases := map[string]struct { + input string + key string + value string + }{ + "Simple value without trailing newline": { + input: "KEY=value", + key: "KEY", + value: "value", + }, + "Value with internal whitespace without trailing newline": { + input: "KEY=value value", + key: "KEY", + value: "value value", + }, + "Value with internal whitespace with trailing newline": { + input: "KEY=value value\n", + key: "KEY", + value: "value value", + }, + "YAML style - value with internal whitespace without trailing newline": { + input: "KEY: value value", + key: "KEY", + value: "value value", + }, + "YAML style - value with internal whitespace with trailing newline": { + input: "KEY: value value\n", + key: "KEY", + value: "value value", + }, + } + + for n, c := range cases { + t.Run(n, func(t *testing.T) { + result, err := Unmarshal(c.input) + if err != nil { + t.Errorf("Input: %q Unexpected error:\t%q", c.input, err) + } + if result[c.key] != c.value { + t.Errorf("Input %q Expected:\t %q/%q\nGot:\t %q", c.input, c.key, c.value, result) + } + }) + } +} diff --git a/root/pkg/mod/github.com/joho/godotenv@v1.5.1/parser.go b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/parser.go new file mode 100644 index 0000000..cc709af --- /dev/null +++ b/root/pkg/mod/github.com/joho/godotenv@v1.5.1/parser.go @@ -0,0 +1,271 @@ +package godotenv + +import ( + "bytes" + "errors" + "fmt" + "regexp" + "strings" + "unicode" +) + +const ( + charComment = '#' + prefixSingleQuote = '\'' + prefixDoubleQuote = '"' + + exportPrefix = "export" +) + +func parseBytes(src []byte, out map[string]string) error { + src = bytes.Replace(src, []byte("\r\n"), []byte("\n"), -1) + cutset := src + for { + cutset = getStatementStart(cutset) + if cutset == nil { + // reached end of file + break + } + + key, left, err := locateKeyName(cutset) + if err != nil { + return err + } + + value, left, err := extractVarValue(left, out) + if err != nil { + return err + } + + out[key] = value + cutset = left + } + + return nil +} + +// getStatementPosition returns position of statement begin. +// +// It skips any comment line or non-whitespace character. +func getStatementStart(src []byte) []byte { + pos := indexOfNonSpaceChar(src) + if pos == -1 { + return nil + } + + src = src[pos:] + if src[0] != charComment { + return src + } + + // skip comment section + pos = bytes.IndexFunc(src, isCharFunc('\n')) + if pos == -1 { + return nil + } + + return getStatementStart(src[pos:]) +} + +// locateKeyName locates and parses key name and returns rest of slice +func locateKeyName(src []byte) (key string, cutset []byte, err error) { + // trim "export" and space at beginning + src = bytes.TrimLeftFunc(src, isSpace) + if bytes.HasPrefix(src, []byte(exportPrefix)) { + trimmed := bytes.TrimPrefix(src, []byte(exportPrefix)) + if bytes.IndexFunc(trimmed, isSpace) == 0 { + src = bytes.TrimLeftFunc(trimmed, isSpace) + } + } + + // locate key name end and validate it in single loop + offset := 0 +loop: + for i, char := range src { + rchar := rune(char) + if isSpace(rchar) { + continue + } + + switch char { + case '=', ':': + // library also supports yaml-style value declaration + key = string(src[0:i]) + offset = i + 1 + break loop + case '_': + default: + // variable name should match [A-Za-z0-9_.] + if unicode.IsLetter(rchar) || unicode.IsNumber(rchar) || rchar == '.' { + continue + } + + return "", nil, fmt.Errorf( + `unexpected character %q in variable name near %q`, + string(char), string(src)) + } + } + + if len(src) == 0 { + return "", nil, errors.New("zero length string") + } + + // trim whitespace + key = strings.TrimRightFunc(key, unicode.IsSpace) + cutset = bytes.TrimLeftFunc(src[offset:], isSpace) + return key, cutset, nil +} + +// extractVarValue extracts variable value and returns rest of slice +func extractVarValue(src []byte, vars map[string]string) (value string, rest []byte, err error) { + quote, hasPrefix := hasQuotePrefix(src) + if !hasPrefix { + // unquoted value - read until end of line + endOfLine := bytes.IndexFunc(src, isLineEnd) + + // Hit EOF without a trailing newline + if endOfLine == -1 { + endOfLine = len(src) + + if endOfLine == 0 { + return "", nil, nil + } + } + + // Convert line to rune away to do accurate countback of runes + line := []rune(string(src[0:endOfLine])) + + // Assume end of line is end of var + endOfVar := len(line) + if endOfVar == 0 { + return "", src[endOfLine:], nil + } + + // Work backwards to check if the line ends in whitespace then + // a comment (ie asdasd # some comment) + for i := endOfVar - 1; i >= 0; i-- { + if line[i] == charComment && i > 0 { + if isSpace(line[i-1]) { + endOfVar = i + break + } + } + } + + trimmed := strings.TrimFunc(string(line[0:endOfVar]), isSpace) + + return expandVariables(trimmed, vars), src[endOfLine:], nil + } + + // lookup quoted string terminator + for i := 1; i < len(src); i++ { + if char := src[i]; char != quote { + continue + } + + // skip escaped quote symbol (\" or \', depends on quote) + if prevChar := src[i-1]; prevChar == '\\' { + continue + } + + // trim quotes + trimFunc := isCharFunc(rune(quote)) + value = string(bytes.TrimLeftFunc(bytes.TrimRightFunc(src[0:i], trimFunc), trimFunc)) + if quote == prefixDoubleQuote { + // unescape newlines for double quote (this is compat feature) + // and expand environment variables + value = expandVariables(expandEscapes(value), vars) + } + + return value, src[i+1:], nil + } + + // return formatted error if quoted string is not terminated + valEndIndex := bytes.IndexFunc(src, isCharFunc('\n')) + if valEndIndex == -1 { + valEndIndex = len(src) + } + + return "", nil, fmt.Errorf("unterminated quoted value %s", src[:valEndIndex]) +} + +func expandEscapes(str string) string { + out := escapeRegex.ReplaceAllStringFunc(str, func(match string) string { + c := strings.TrimPrefix(match, `\`) + switch c { + case "n": + return "\n" + case "r": + return "\r" + default: + return match + } + }) + return unescapeCharsRegex.ReplaceAllString(out, "$1") +} + +func indexOfNonSpaceChar(src []byte) int { + return bytes.IndexFunc(src, func(r rune) bool { + return !unicode.IsSpace(r) + }) +} + +// hasQuotePrefix reports whether charset starts with single or double quote and returns quote character +func hasQuotePrefix(src []byte) (prefix byte, isQuored bool) { + if len(src) == 0 { + return 0, false + } + + switch prefix := src[0]; prefix { + case prefixDoubleQuote, prefixSingleQuote: + return prefix, true + default: + return 0, false + } +} + +func isCharFunc(char rune) func(rune) bool { + return func(v rune) bool { + return v == char + } +} + +// isSpace reports whether the rune is a space character but not line break character +// +// this differs from unicode.IsSpace, which also applies line break as space +func isSpace(r rune) bool { + switch r { + case '\t', '\v', '\f', '\r', ' ', 0x85, 0xA0: + return true + } + return false +} + +func isLineEnd(r rune) bool { + if r == '\n' || r == '\r' { + return true + } + return false +} + +var ( + escapeRegex = regexp.MustCompile(`\\.`) + expandVarRegex = regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`) + unescapeCharsRegex = regexp.MustCompile(`\\([^$])`) +) + +func expandVariables(v string, m map[string]string) string { + return expandVarRegex.ReplaceAllStringFunc(v, func(s string) string { + submatch := expandVarRegex.FindStringSubmatch(s) + + if submatch == nil { + return s + } + if submatch[1] == "\\" || submatch[2] == "(" { + return submatch[0][1:] + } else if submatch[4] != "" { + return m[submatch[4]] + } + return s + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/.circleci/config.yml b/root/pkg/mod/gotest.tools/v3@v3.3.0/.circleci/config.yml new file mode 100644 index 0000000..f5ad793 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/.circleci/config.yml @@ -0,0 +1,63 @@ +version: 2.1 + +orbs: + go: gotest/tools@0.0.14 + +workflows: + ci: + jobs: + - lint + - go/test: + name: test-golang-1.15 + executor: + name: go/golang + tag: 1.15-alpine + - go/test: + name: test-golang-1.16 + executor: + name: go/golang + tag: 1.16-alpine + - go/test: + name: test-golang-1.17 + executor: + name: go/golang + tag: 1.17-alpine + - go/test: + name: test-golang-1.18 + executor: + name: go/golang + tag: 1.18-alpine + - go/test: + name: test-windows + executor: windows + pre-steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - run: | + choco upgrade golang + echo 'export PATH="$PATH:/c/Program Files/Go/bin"' > $BASH_ENV + - run: go version + +executors: + windows: + machine: + image: windows-server-2019-vs2019:stable + resource_class: windows.medium + shell: bash.exe + +jobs: + + lint: + executor: + name: go/golang + tag: 1.18-alpine + steps: + - checkout + - go/install-golangci-lint: + prefix: v1.45.2 + version: 1.45.2 + - go/install: {package: git} + - run: + name: Lint + command: golangci-lint run -v --concurrency 2 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/.codecov.yml b/root/pkg/mod/gotest.tools/v3@v3.3.0/.codecov.yml new file mode 100644 index 0000000..4dd7324 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/.codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + threshold: 2 + patch: + default: + threshold: 20 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/.github/workflows/sync-main.yaml b/root/pkg/mod/gotest.tools/v3@v3.3.0/.github/workflows/sync-main.yaml new file mode 100644 index 0000000..3f052d0 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/.github/workflows/sync-main.yaml @@ -0,0 +1,18 @@ +name: Merge main into master + +on: + push: + branches: [main] + +jobs: + sync: + name: Merge main branch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: {fetch-depth: 0} + - name: merge + run: | + git checkout master + git merge --ff-only main + git push origin master diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/.gitignore b/root/pkg/mod/gotest.tools/v3@v3.3.0/.gitignore new file mode 100644 index 0000000..97a6510 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/.gitignore @@ -0,0 +1,5 @@ +vendor/ +.dobi/ +Gopkg.lock +.depsources +dist/ diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/.golangci.yml b/root/pkg/mod/gotest.tools/v3@v3.3.0/.golangci.yml new file mode 100644 index 0000000..139e1af --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/.golangci.yml @@ -0,0 +1,68 @@ +linters-settings: + goconst: + min-len: 5 + min-occurrences: 10 + lll: + line-length: 100 + maintidx: + under: 40 + +issues: + exclude-use-default: false + exclude-rules: + - text: 'result .* is always' + linters: [unparam] + - text: 'always receives' + linters: [unparam] + - path: _test\.go + linters: [errcheck, staticcheck, lll, maintidx] + - path: internal/difflib/difflib\.go + text: . + - text: 'return value of .*Close` is not checked' + linters: [errcheck] + - text: 'SA1019' + linters: [staticcheck] + - path: internal/ + text: 'ST1000' + linters: [stylecheck] + - path: 'example_test\.go' + linters: [bodyclose] + +linters: + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - errcheck + - errorlint + - exportloopref + - gocognit + - goconst + - gofmt + - goimports + - golint + - gosimple + - govet + - ineffassign + - interfacer + - lll + - maintidx + - misspell + - nakedret + - nestif + - nilerr + - nilnil + - nolintlint + - prealloc + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - wastedassign + - whitespace diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/CONTRIBUTING.md b/root/pkg/mod/gotest.tools/v3@v3.3.0/CONTRIBUTING.md new file mode 100644 index 0000000..d157c77 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/CONTRIBUTING.md @@ -0,0 +1,12 @@ +# Contributing to gotest.tools + +Thank you for your interest in contributing to the project! Below are some +suggestions which may make the process easier. + +## Pull requests + +Pull requests for new features should generally be preceded by an issue +explaining the feature and why it is necessary. + +Pull requests for bug fixes are always appreciated. They should almost always +include a test which fails without the bug fix. diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/Dockerfile b/root/pkg/mod/gotest.tools/v3@v3.3.0/Dockerfile new file mode 100644 index 0000000..72942fd --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/Dockerfile @@ -0,0 +1,33 @@ + +ARG GOLANG_VERSION +FROM golang:${GOLANG_VERSION:-1.12-alpine} as golang +RUN apk add -U curl git bash +WORKDIR /go/src/gotest.tools +ENV CGO_ENABLED=0 \ + PS1="# " \ + GO111MODULE=on + +FROM golang as tools +RUN go get github.com/dnephin/filewatcher@v0.3.2 + +ARG DEP_TAG=v0.4.1 +RUN export GO111MODULE=off; \ + go get -d github.com/golang/dep/cmd/dep && \ + cd /go/src/github.com/golang/dep && \ + git checkout -q "$DEP_TAG" && \ + go build -o /usr/bin/dep ./cmd/dep + +RUN go get gotest.tools/gotestsum@v0.3.3 +RUN wget -O- -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s && \ + mv bin/golangci-lint /go/bin + + +FROM golang as dev +COPY --from=tools /go/bin/filewatcher /usr/bin/filewatcher +COPY --from=tools /usr/bin/dep /usr/bin/dep +COPY --from=tools /go/bin/gotestsum /usr/bin/gotestsum +COPY --from=tools /go/bin/golangci-lint /usr/bin/golangci-lint + + +FROM dev as dev-with-source +COPY . . diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/LICENSE b/root/pkg/mod/gotest.tools/v3@v3.3.0/LICENSE new file mode 100644 index 0000000..aeaa2fa --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/LICENSE @@ -0,0 +1,13 @@ +Copyright 2018 gotest.tools authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/README.md b/root/pkg/mod/gotest.tools/v3@v3.3.0/README.md new file mode 100644 index 0000000..d9876aa --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/README.md @@ -0,0 +1,53 @@ +# gotest.tools + +A collection of packages to augment `testing` and support common patterns. + +[![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://pkg.go.dev/gotest.tools/v3/?tab=subdirectories) +[![CircleCI](https://circleci.com/gh/gotestyourself/gotest.tools/tree/main.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotest.tools/tree/main) +[![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools) + +## Usage + +With Go modules enabled (go1.11+) + +``` +$ go get gotest.tools/v3 +``` + +``` +import "gotest.tools/v3/assert" +``` + +To use `gotest.tools` with an older version of Go that does not understand Go +module paths pin to version `v2.3.0`. + + +## Packages + +* [assert](http://pkg.go.dev/gotest.tools/v3/assert) - + compare values and fail the test when a comparison fails +* [env](http://pkg.go.dev/gotest.tools/v3/env) - + test code which uses environment variables +* [fs](http://pkg.go.dev/gotest.tools/v3/fs) - + create temporary files and compare a filesystem tree to an expected value +* [golden](http://pkg.go.dev/gotest.tools/v3/golden) - + compare large multi-line strings against values frozen in golden files +* [icmd](http://pkg.go.dev/gotest.tools/v3/icmd) - + execute binaries and test the output +* [poll](http://pkg.go.dev/gotest.tools/v3/poll) - + test asynchronous code by polling until a desired state is reached +* [skip](http://pkg.go.dev/gotest.tools/v3/skip) - + skip a test and print the source code of the condition used to skip the test + +## Related + +* [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - + go test runner with custom output +* [go testing patterns](https://github.com/gotestyourself/gotest.tools/wiki/Go-Testing-Patterns) - + zero-depedency patterns for organizing test cases +* [test doubles and patching](https://github.com/gotestyourself/gotest.tools/wiki/Test-Doubles-And-Patching) - + zero-depdency test doubles (fakes, spies, stubs, and mocks) and monkey patching patterns + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert.go new file mode 100644 index 0000000..dbd4f5a --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert.go @@ -0,0 +1,314 @@ +/*Package assert provides assertions for comparing expected values to actual +values in tests. When an assertion fails a helpful error message is printed. + +Example usage + +All the assertions in this package use testing.T.Helper to mark themselves as +test helpers. This allows the testing package to print the filename and line +number of the file function that failed. + + assert.NilError(t, err) + // filename_test.go:212: assertion failed: error is not nil: file not found + +If any assertion is called from a helper function, make sure to call t.Helper +from the helper function so that the filename and line number remain correct. + +The examples below show assert used with some common types and the failure +messages it produces. The filename and line number portion of the failure +message is omitted from these examples for brevity. + + // booleans + + assert.Assert(t, ok) + // assertion failed: ok is false + assert.Assert(t, !missing) + // assertion failed: missing is true + + // primitives + + assert.Equal(t, count, 1) + // assertion failed: 0 (count int) != 1 (int) + assert.Equal(t, msg, "the message") + // assertion failed: my message (msg string) != the message (string) + assert.Assert(t, total != 10) // use Assert for NotEqual + // assertion failed: total is 10 + assert.Assert(t, count > 20, "count=%v", count) + // assertion failed: count is <= 20: count=1 + + // errors + + assert.NilError(t, closer.Close()) + // assertion failed: error is not nil: close /file: errno 11 + assert.Error(t, err, "the exact error message") + // assertion failed: expected error "the exact error message", got "oops" + assert.ErrorContains(t, err, "includes this") + // assertion failed: expected error to contain "includes this", got "oops" + assert.ErrorIs(t, err, os.ErrNotExist) + // assertion failed: error is "oops", not "file does not exist" (os.ErrNotExist) + + // complex types + + assert.DeepEqual(t, result, myStruct{Name: "title"}) + // assertion failed: ... (diff of the two structs) + assert.Assert(t, is.Len(items, 3)) + // assertion failed: expected [] (length 0) to have length 3 + assert.Assert(t, len(sequence) != 0) // use Assert for NotEmpty + // assertion failed: len(sequence) is 0 + assert.Assert(t, is.Contains(mapping, "key")) + // assertion failed: map[other:1] does not contain key + + // pointers and interface + + assert.Assert(t, ref == nil) + // assertion failed: ref is not nil + assert.Assert(t, ref != nil) // use Assert for NotNil + // assertion failed: ref is nil + +Assert and Check + +Assert and Check are very similar, they both accept a Comparison, and fail +the test when the comparison fails. The one difference is that Assert uses +testing.T.FailNow to fail the test, which will end the test execution immediately. +Check uses testing.T.Fail to fail the test, which allows it to return the +result of the comparison, then proceed with the rest of the test case. + +Like testing.T.FailNow, Assert must be called from the goroutine running the test, +not from other goroutines created during the test. Check is safe to use from any +goroutine. + +Comparisons + +Package http://pkg.go.dev/gotest.tools/v3/assert/cmp provides +many common comparisons. Additional comparisons can be written to compare +values in other ways. See the example Assert (CustomComparison). + +Automated migration from testify + +gty-migrate-from-testify is a command which translates Go source code from +testify assertions to the assertions provided by this package. + +See http://pkg.go.dev/gotest.tools/v3/assert/cmd/gty-migrate-from-testify. + + +*/ +package assert // import "gotest.tools/v3/assert" + +import ( + gocmp "github.com/google/go-cmp/cmp" + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/assert" +) + +// BoolOrComparison can be a bool, cmp.Comparison, or error. See Assert for +// details about how this type is used. +type BoolOrComparison interface{} + +// TestingT is the subset of testing.T used by the assert package. +type TestingT interface { + FailNow() + Fail() + Log(args ...interface{}) +} + +type helperT interface { + Helper() +} + +// Assert performs a comparison. If the comparison fails, the test is marked as +// failed, a failure message is logged, and execution is stopped immediately. +// +// The comparison argument may be one of three types: +// +// bool +// True is success. False is a failure. The failure message will contain +// the literal source code of the expression. +// +// cmp.Comparison +// Uses cmp.Result.Success() to check for success or failure. +// The comparison is responsible for producing a helpful failure message. +// http://pkg.go.dev/gotest.tools/v3/assert/cmp provides many common comparisons. +// +// error +// A nil value is considered success, and a non-nil error is a failure. +// The return value of error.Error is used as the failure message. +// +// +// Extra details can be added to the failure message using msgAndArgs. msgAndArgs +// may be either a single string, or a format string and args that will be +// passed to fmt.Sprintf. +// +// Assert uses t.FailNow to fail the test. Like t.FailNow, Assert must be called +// from the goroutine running the test function, not from other +// goroutines created during the test. Use Check from other goroutines. +func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsFromComparisonCall, comparison, msgAndArgs...) { + t.FailNow() + } +} + +// Check performs a comparison. If the comparison fails the test is marked as +// failed, a failure message is printed, and Check returns false. If the comparison +// is successful Check returns true. Check may be called from any goroutine. +// +// See Assert for details about the comparison arg and failure messages. +func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsFromComparisonCall, comparison, msgAndArgs...) { + t.Fail() + return false + } + return true +} + +// NilError fails the test immediately if err is not nil, and includes err.Error +// in the failure message. +// +// NilError uses t.FailNow to fail the test. Like t.FailNow, NilError must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check from other goroutines. +func NilError(t TestingT, err error, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, err, msgAndArgs...) { + t.FailNow() + } +} + +// Equal uses the == operator to assert two values are equal and fails the test +// if they are not equal. +// +// If the comparison fails Equal will use the variable names and types of +// x and y as part of the failure message to identify the actual and expected +// values. +// +// assert.Equal(t, actual, expected) +// // main_test.go:41: assertion failed: 1 (actual int) != 21 (expected int32) +// +// If either x or y are a multi-line string the failure message will include a +// unified diff of the two values. If the values only differ by whitespace +// the unified diff will be augmented by replacing whitespace characters with +// visible characters to identify the whitespace difference. +// +// Equal uses t.FailNow to fail the test. Like t.FailNow, Equal must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check with cmp.Equal from other +// goroutines. +func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.Equal(x, y), msgAndArgs...) { + t.FailNow() + } +} + +// DeepEqual uses google/go-cmp (https://godoc.org/github.com/google/go-cmp/cmp) +// to assert two values are equal and fails the test if they are not equal. +// +// Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional +// commonly used Options. +// +// DeepEqual uses t.FailNow to fail the test. Like t.FailNow, DeepEqual must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check with cmp.DeepEqual from other +// goroutines. +func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.DeepEqual(x, y, opts...)) { + t.FailNow() + } +} + +// Error fails the test if err is nil, or if err.Error is not equal to expected. +// Both err.Error and expected will be included in the failure message. +// Error performs an exact match of the error text. Use ErrorContains if only +// part of the error message is relevant. Use ErrorType or ErrorIs to compare +// errors by type. +// +// Error uses t.FailNow to fail the test. Like t.FailNow, Error must be +// called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check with cmp.Error from other +// goroutines. +func Error(t TestingT, err error, expected string, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.Error(err, expected), msgAndArgs...) { + t.FailNow() + } +} + +// ErrorContains fails the test if err is nil, or if err.Error does not +// contain the expected substring. Both err.Error and the expected substring +// will be included in the failure message. +// +// ErrorContains uses t.FailNow to fail the test. Like t.FailNow, ErrorContains +// must be called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check with cmp.ErrorContains from other +// goroutines. +func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...) { + t.FailNow() + } +} + +// ErrorType fails the test if err is nil, or err is not the expected type. +// Most new code should use ErrorIs instead. ErrorType may be deprecated in the +// future. +// +// Expected can be one of: +// +// func(error) bool +// The function should return true if the error is the expected type. +// +// struct{} or *struct{} +// A struct or a pointer to a struct. The assertion fails if the error is +// not of the same type. +// +// *interface{} +// A pointer to an interface type. The assertion fails if err does not +// implement the interface. +// +// reflect.Type +// The assertion fails if err does not implement the reflect.Type. +// +// ErrorType uses t.FailNow to fail the test. Like t.FailNow, ErrorType +// must be called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check with cmp.ErrorType from other +// goroutines. +func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorType(err, expected), msgAndArgs...) { + t.FailNow() + } +} + +// ErrorIs fails the test if err is nil, or the error does not match expected +// when compared using errors.Is. See https://golang.org/pkg/errors/#Is for +// accepted arguments. +// +// ErrorIs uses t.FailNow to fail the test. Like t.FailNow, ErrorIs +// must be called from the goroutine running the test function, not from other +// goroutines created during the test. Use Check with cmp.ErrorIs from other +// goroutines. +func ErrorIs(t TestingT, err error, expected error, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorIs(err, expected), msgAndArgs...) { + t.FailNow() + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_ext_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_ext_test.go new file mode 100644 index 0000000..5903f70 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_ext_test.go @@ -0,0 +1,112 @@ +package assert_test + +import ( + "go/parser" + "go/token" + "io/ioutil" + "runtime" + "strings" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/internal/source" +) + +func TestEqual_WithGoldenUpdate(t *testing.T) { + t.Run("assert failed with -update=false", func(t *testing.T) { + ft := &fakeTestingT{} + actual := `not this value` + assert.Equal(ft, actual, expectedOne) + assert.Assert(t, ft.failNowed) + }) + + t.Run("var is updated when -update=true", func(t *testing.T) { + patchUpdate(t) + t.Cleanup(func() { + resetVariable(t, "expectedOne", "") + }) + + actual := `this is the +actual value +that we are testing +` + assert.Equal(t, actual, expectedOne) + + raw, err := ioutil.ReadFile(fileName(t)) + assert.NilError(t, err) + + expected := "var expectedOne = `this is the\nactual value\nthat we are testing\n`" + assert.Assert(t, strings.Contains(string(raw), expected), "actual=%v", string(raw)) + }) + + t.Run("const is updated when -update=true", func(t *testing.T) { + patchUpdate(t) + t.Cleanup(func() { + resetVariable(t, "expectedTwo", "") + }) + + actual := `this is the new +expected value +` + assert.Equal(t, actual, expectedTwo) + + raw, err := ioutil.ReadFile(fileName(t)) + assert.NilError(t, err) + + expected := "const expectedTwo = `this is the new\nexpected value\n`" + assert.Assert(t, strings.Contains(string(raw), expected), "actual=%v", string(raw)) + }) +} + +// expectedOne is updated by running the tests with -update +var expectedOne = `` + +// expectedTwo is updated by running the tests with -update +const expectedTwo = `` + +func patchUpdate(t *testing.T) { + source.Update = true + t.Cleanup(func() { + source.Update = false + }) +} + +func fileName(t *testing.T) string { + t.Helper() + _, filename, _, ok := runtime.Caller(1) + assert.Assert(t, ok, "failed to get call stack") + return filename +} + +func resetVariable(t *testing.T, varName string, value string) { + t.Helper() + _, filename, _, ok := runtime.Caller(1) + assert.Assert(t, ok, "failed to get call stack") + + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors|parser.ParseComments) + assert.NilError(t, err) + + err = source.UpdateVariable(filename, fileset, astFile, varName, value) + assert.NilError(t, err, "failed to reset file") +} + +type fakeTestingT struct { + failNowed bool + failed bool + msgs []string +} + +func (f *fakeTestingT) FailNow() { + f.failNowed = true +} + +func (f *fakeTestingT) Fail() { + f.failed = true +} + +func (f *fakeTestingT) Log(args ...interface{}) { + f.msgs = append(f.msgs, args[0].(string)) +} + +func (f *fakeTestingT) Helper() {} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_test.go new file mode 100644 index 0000000..e8cd901 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/assert_test.go @@ -0,0 +1,470 @@ +package assert + +import ( + "fmt" + "os" + "testing" + + gocmp "github.com/google/go-cmp/cmp" + "gotest.tools/v3/assert/cmp" +) + +type fakeTestingT struct { + failNowed bool + failed bool + msgs []string +} + +func (f *fakeTestingT) FailNow() { + f.failNowed = true +} + +func (f *fakeTestingT) Fail() { + f.failed = true +} + +func (f *fakeTestingT) Log(args ...interface{}) { + f.msgs = append(f.msgs, args[0].(string)) +} + +func (f *fakeTestingT) Helper() {} + +func TestAssert_WithBinaryExpression_Failures(t *testing.T) { + t.Run("equal", func(t *testing.T) { + fakeT := &fakeTestingT{} + Assert(fakeT, 1 == 6) + expectFailNowed(t, fakeT, "assertion failed: 1 is not 6") + }) + t.Run("not equal", func(t *testing.T) { + fakeT := &fakeTestingT{} + a := 1 + Assert(fakeT, a != 1) + expectFailNowed(t, fakeT, "assertion failed: a is 1") + }) + t.Run("greater than", func(t *testing.T) { + fakeT := &fakeTestingT{} + Assert(fakeT, 1 > 5) + expectFailNowed(t, fakeT, "assertion failed: 1 is <= 5") + }) + t.Run("less than", func(t *testing.T) { + fakeT := &fakeTestingT{} + Assert(fakeT, 5 < 1) + expectFailNowed(t, fakeT, "assertion failed: 5 is >= 1") + }) + t.Run("greater than or equal", func(t *testing.T) { + fakeT := &fakeTestingT{} + Assert(fakeT, 1 >= 5) + expectFailNowed(t, fakeT, "assertion failed: 1 is less than 5") + }) + t.Run("less than or equal", func(t *testing.T) { + fakeT := &fakeTestingT{} + Assert(fakeT, 6 <= 2) + expectFailNowed(t, fakeT, "assertion failed: 6 is greater than 2") + }) +} + +func TestAssertWithBoolIdent(t *testing.T) { + fakeT := &fakeTestingT{} + + var ok bool + Assert(fakeT, ok) + expectFailNowed(t, fakeT, "assertion failed: ok is false") +} + +func TestAssertWithBoolFailureNotEqual(t *testing.T) { + fakeT := &fakeTestingT{} + + var err error + Assert(fakeT, err != nil) + expectFailNowed(t, fakeT, "assertion failed: err is nil") +} + +func TestAssertWithBoolFailureNotTrue(t *testing.T) { + fakeT := &fakeTestingT{} + + badNews := true + Assert(fakeT, !badNews) + expectFailNowed(t, fakeT, "assertion failed: badNews is true") +} + +func TestAssertWithBoolFailureAndExtraMessage(t *testing.T) { + fakeT := &fakeTestingT{} + + Assert(fakeT, 1 > 5, "sometimes things fail") + expectFailNowed(t, fakeT, "assertion failed: 1 is <= 5: sometimes things fail") +} + +func TestAssertWithBoolSuccess(t *testing.T) { + fakeT := &fakeTestingT{} + + Assert(fakeT, 1 < 5) + expectSuccess(t, fakeT) +} + +func TestAssertWithBoolMultiLineFailure(t *testing.T) { + fakeT := &fakeTestingT{} + + Assert(fakeT, func() bool { + for range []int{1, 2, 3, 4} { + } + return false + }()) + expectFailNowed(t, fakeT, `assertion failed: expression is false: func() bool { + for range []int{1, 2, 3, 4} { + } + return false +}()`) +} + +type exampleComparison struct { + success bool + message string +} + +func (c exampleComparison) Compare() (bool, string) { + return c.success, c.message +} + +func TestAssertWithComparisonSuccess(t *testing.T) { + fakeT := &fakeTestingT{} + + cmp := exampleComparison{success: true} + Assert(fakeT, cmp.Compare) + expectSuccess(t, fakeT) +} + +func TestAssertWithComparisonFailure(t *testing.T) { + fakeT := &fakeTestingT{} + + cmp := exampleComparison{message: "oops, not good"} + Assert(fakeT, cmp.Compare) + expectFailNowed(t, fakeT, "assertion failed: oops, not good") +} + +func TestAssertWithComparisonAndExtraMessage(t *testing.T) { + fakeT := &fakeTestingT{} + + cmp := exampleComparison{message: "oops, not good"} + Assert(fakeT, cmp.Compare, "extra stuff %v", true) + expectFailNowed(t, fakeT, "assertion failed: oops, not good: extra stuff true") +} + +type customError struct { + field bool +} + +func (e *customError) Error() string { + // access a field of the receiver to simulate the behaviour of most + // implementations, and test handling of non-nil typed errors. + e.field = true + return "custom error" +} + +func TestNilError(t *testing.T) { + t.Run("nil interface", func(t *testing.T) { + fakeT := &fakeTestingT{} + var err error + NilError(fakeT, err) + expectSuccess(t, fakeT) + }) + + t.Run("nil literal", func(t *testing.T) { + fakeT := &fakeTestingT{} + NilError(fakeT, nil) + expectSuccess(t, fakeT) + }) + + t.Run("interface with non-nil type", func(t *testing.T) { + fakeT := &fakeTestingT{} + var customErr *customError + NilError(fakeT, customErr) + expected := "assertion failed: error is not nil: error has type *assert.customError" + expectFailNowed(t, fakeT, expected) + }) + + t.Run("non-nil error", func(t *testing.T) { + fakeT := &fakeTestingT{} + NilError(fakeT, fmt.Errorf("this is the error")) + expectFailNowed(t, fakeT, "assertion failed: error is not nil: this is the error") + }) + + t.Run("non-nil error with struct type", func(t *testing.T) { + fakeT := &fakeTestingT{} + err := structError{} + NilError(fakeT, err) + expectFailNowed(t, fakeT, "assertion failed: error is not nil: this is a struct") + }) + + t.Run("non-nil error with map type", func(t *testing.T) { + fakeT := &fakeTestingT{} + var err mapError + NilError(fakeT, err) + expectFailNowed(t, fakeT, "assertion failed: error is not nil: ") + }) +} + +type structError struct{} + +func (structError) Error() string { + return "this is a struct" +} + +type mapError map[int]string + +func (m mapError) Error() string { + return m[0] +} + +func TestCheckFailure(t *testing.T) { + fakeT := &fakeTestingT{} + + if Check(fakeT, 1 == 2) { + t.Error("expected check to return false on failure") + } + expectFailed(t, fakeT, "assertion failed: 1 is not 2") +} + +func TestCheckSuccess(t *testing.T) { + fakeT := &fakeTestingT{} + + if !Check(fakeT, true) { + t.Error("expected check to return true on success") + } + expectSuccess(t, fakeT) +} + +func TestCheckEqualFailure(t *testing.T) { + fakeT := &fakeTestingT{} + + actual, expected := 5, 9 + Check(fakeT, cmp.Equal(actual, expected)) + expectFailed(t, fakeT, "assertion failed: 5 (actual int) != 9 (expected int)") +} + +func TestCheck_MultipleFunctionsOnTheSameLine(t *testing.T) { + fakeT := &fakeTestingT{} + + f := func(b bool) {} + f(Check(fakeT, false)) + // TODO: update the expected when there is a more correct fix + expectFailed(t, fakeT, + "assertion failed: but assert failed to find the expression to print") +} + +func TestEqualSuccess(t *testing.T) { + fakeT := &fakeTestingT{} + + Equal(fakeT, 1, 1) + expectSuccess(t, fakeT) + + Equal(fakeT, "abcd", "abcd") + expectSuccess(t, fakeT) +} + +func TestEqualFailure(t *testing.T) { + fakeT := &fakeTestingT{} + + actual, expected := 1, 3 + Equal(fakeT, actual, expected) + expectFailNowed(t, fakeT, "assertion failed: 1 (actual int) != 3 (expected int)") +} + +func TestEqualFailureTypes(t *testing.T) { + fakeT := &fakeTestingT{} + + Equal(fakeT, 3, uint(3)) + expectFailNowed(t, fakeT, `assertion failed: 3 (int) != 3 (uint)`) +} + +func TestEqualFailureWithSelectorArgument(t *testing.T) { + fakeT := &fakeTestingT{} + + type tc struct { + expected string + } + var testcase = tc{expected: "foo"} + + Equal(fakeT, "ok", testcase.expected) + expectFailNowed(t, fakeT, + "assertion failed: ok (string) != foo (testcase.expected string)") +} + +func TestEqualFailureWithIndexExpr(t *testing.T) { + fakeT := &fakeTestingT{} + + expected := map[string]string{"foo": "bar"} + Equal(fakeT, "ok", expected["foo"]) + expectFailNowed(t, fakeT, + `assertion failed: ok (string) != bar (expected["foo"] string)`) +} + +func TestEqualFailureWithCallExprArgument(t *testing.T) { + fakeT := &fakeTestingT{} + ce := customError{} + Equal(fakeT, "", ce.Error()) + expectFailNowed(t, fakeT, + "assertion failed: (string) != custom error (string)") +} + +func TestAssertFailureWithOfflineComparison(t *testing.T) { + fakeT := &fakeTestingT{} + a := 1 + b := 2 + // store comparison in a variable, so ast lookup can't find it + comparison := cmp.Equal(a, b) + Assert(fakeT, comparison) + // expected value wont have variable names + expectFailNowed(t, fakeT, "assertion failed: 1 (int) != 2 (int)") +} + +type testingT interface { + Errorf(msg string, args ...interface{}) + Fatalf(msg string, args ...interface{}) +} + +func expectFailNowed(t testingT, fakeT *fakeTestingT, expected string) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if fakeT.failed { + t.Errorf("should not have failed, got messages %s", fakeT.msgs) + } + if !fakeT.failNowed { + t.Fatalf("should have failNowed with message %s", expected) + } + if fakeT.msgs[0] != expected { + t.Fatalf("should have failure message %q, got %q", expected, fakeT.msgs[0]) + } +} + +func expectFailed(t testingT, fakeT *fakeTestingT, expected string) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if fakeT.failNowed { + t.Errorf("should not have failNowed, got messages %s", fakeT.msgs) + } + if !fakeT.failed { + t.Fatalf("should have failed with message %s", expected) + } + if fakeT.msgs[0] != expected { + t.Fatalf("should have failure message %q, got %q", expected, fakeT.msgs[0]) + } +} + +func expectSuccess(t testingT, fakeT *fakeTestingT) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if fakeT.failNowed { + t.Errorf("should not have failNowed, got messages %s", fakeT.msgs) + } + if fakeT.failed { + t.Errorf("should not have failed, got messages %s", fakeT.msgs) + } +} + +type stub struct { + a string + b int +} + +func TestDeepEqualSuccess(t *testing.T) { + actual := stub{"ok", 1} + expected := stub{"ok", 1} + + fakeT := &fakeTestingT{} + DeepEqual(fakeT, actual, expected, gocmp.AllowUnexported(stub{})) + expectSuccess(t, fakeT) +} + +func TestDeepEqualFailure(t *testing.T) { + actual := stub{"ok", 1} + expected := stub{"ok", 2} + + fakeT := &fakeTestingT{} + DeepEqual(fakeT, actual, expected, gocmp.AllowUnexported(stub{})) + if !fakeT.failNowed { + t.Fatal("should have failNowed") + } +} + +func TestErrorFailure(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + var err error + Error(fakeT, err, "this error") + expectFailNowed(t, fakeT, "assertion failed: expected an error, got nil") + }) + t.Run("different error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("the actual error") + Error(fakeT, err, "this error") + expected := `assertion failed: expected error "this error", got "the actual error"` + expectFailNowed(t, fakeT, expected) + }) +} + +func TestErrorContainsFailure(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + var err error + ErrorContains(fakeT, err, "this error") + expectFailNowed(t, fakeT, "assertion failed: expected an error, got nil") + }) + t.Run("different error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("the actual error") + ErrorContains(fakeT, err, "this error") + expected := `assertion failed: expected error to contain "this error", got "the actual error"` + expectFailNowed(t, fakeT, expected) + }) +} + +func TestErrorTypeFailure(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + var err error + ErrorType(fakeT, err, os.IsNotExist) + expectFailNowed(t, fakeT, "assertion failed: error is nil, not os.IsNotExist") + }) + t.Run("different error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("the actual error") + ErrorType(fakeT, err, os.IsNotExist) + expected := `assertion failed: error is the actual error (*errors.errorString), not os.IsNotExist` + expectFailNowed(t, fakeT, expected) + }) +} + +func TestErrorIs(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + var err error + ErrorIs(fakeT, err, os.ErrNotExist) + expected := `assertion failed: error is nil, not "file does not exist" (os.ErrNotExist)` + expectFailNowed(t, fakeT, expected) + }) + t.Run("different error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("the actual error") + ErrorIs(fakeT, err, os.ErrNotExist) + expected := `assertion failed: error is "the actual error", not "file does not exist" (os.ErrNotExist)` + expectFailNowed(t, fakeT, expected) + }) + t.Run("same error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("some wrapping: %w", os.ErrNotExist) + ErrorIs(fakeT, err, os.ErrNotExist) + expectSuccess(t, fakeT) + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call.go new file mode 100644 index 0000000..c93997b --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call.go @@ -0,0 +1,201 @@ +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/token" +) + +// call wraps a testify/assert ast.CallExpr and exposes properties of the +// expression to facilitate migrating the expression to a gotest.tools/v3/assert +type call struct { + fileset *token.FileSet + expr *ast.CallExpr + xIdent *ast.Ident + selExpr *ast.SelectorExpr + // assert is Assert (if the testify package was require), or Check (if the + // testify package was assert). + assert string +} + +func (c call) String() string { + buf := new(bytes.Buffer) + // nolint: errcheck + format.Node(buf, token.NewFileSet(), c.expr) + return buf.String() +} + +func (c call) StringWithFileInfo() string { + if c.fileset.File(c.expr.Pos()) == nil { + return fmt.Sprintf("%s at unknown file", c) + } + return fmt.Sprintf("%s at %s:%d", c, + relativePath(c.fileset.File(c.expr.Pos()).Name()), + c.fileset.Position(c.expr.Pos()).Line) +} + +// testingT returns the first argument of the call, which is assumed to be a +// *ast.Ident of *testing.T or a compatible interface. +func (c call) testingT() ast.Expr { + if len(c.expr.Args) == 0 { + return nil + } + return c.expr.Args[0] +} + +// extraArgs returns the arguments of the expression starting from index +func (c call) extraArgs(index int) []ast.Expr { + if len(c.expr.Args) <= index { + return nil + } + return c.expr.Args[index:] +} + +// args returns a range of arguments from the expression +func (c call) args(from, to int) []ast.Expr { + return c.expr.Args[from:to] +} + +// arg returns a single argument from the expression +func (c call) arg(index int) ast.Expr { + return c.expr.Args[index] +} + +// newCallFromCallExpr returns a new call build from a ast.CallExpr. Returns false +// if the call expression does not have the expected ast nodes. +func newCallFromCallExpr(callExpr *ast.CallExpr, migration migration) (call, bool) { + c := call{} + selector, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + return c, false + } + ident, ok := selector.X.(*ast.Ident) + if !ok { + return c, false + } + + return call{ + fileset: migration.fileset, + xIdent: ident, + selExpr: selector, + expr: callExpr, + }, true +} + +// newTestifyCallFromNode returns a call that wraps a valid testify assertion. +// Returns false if the call expression is not a testify assertion. +func newTestifyCallFromNode(callExpr *ast.CallExpr, migration migration) (call, bool) { + tcall, ok := newCallFromCallExpr(callExpr, migration) + if !ok { + return tcall, false + } + + testifyNewAssignStmt := testifyAssertionsAssignment(tcall, migration) + switch { + case testifyNewAssignStmt != nil: + return updateCallForTestifyNew(tcall, testifyNewAssignStmt, migration) + case isTestifyPkgCall(tcall, migration): + tcall.assert = migration.importNames.funcNameFromTestifyName(tcall.xIdent.Name) + return tcall, true + } + return tcall, false +} + +// isTestifyPkgCall returns true if the call is a testify package-level assertion +// (as apposed to an assertion method on the Assertions type) +// +// TODO: check if the xIdent.Obj.Decl is an import declaration instead of +// assuming that a name matching the import name is always an import. Some code +// may shadow import names, which could lead to incorrect results. +func isTestifyPkgCall(tcall call, migration migration) bool { + return migration.importNames.matchesTestify(tcall.xIdent) +} + +// testifyAssertionsAssignment returns an ast.AssignStmt if the call is a testify +// call from an Assertions object returned from assert.New(t) (not a package level +// assert). Otherwise returns nil. +func testifyAssertionsAssignment(tcall call, migration migration) *ast.AssignStmt { + if tcall.xIdent.Obj == nil { + return nil + } + + assignStmt, ok := tcall.xIdent.Obj.Decl.(*ast.AssignStmt) + if !ok { + return nil + } + + if isAssignmentFromAssertNew(assignStmt, migration) { + return assignStmt + } + return nil +} + +func updateCallForTestifyNew( + tcall call, + testifyNewAssignStmt *ast.AssignStmt, + migration migration, +) (call, bool) { + testifyNewCallExpr := callExprFromAssignment(testifyNewAssignStmt) + if testifyNewCallExpr == nil { + return tcall, false + } + testifyNewCall, ok := newCallFromCallExpr(testifyNewCallExpr, migration) + if !ok { + return tcall, false + } + + tcall.assert = migration.importNames.funcNameFromTestifyName(testifyNewCall.xIdent.Name) + tcall.expr = addMissingTestingTArgToCallExpr(tcall.expr, testifyNewCall.testingT()) + return tcall, true +} + +// addMissingTestingTArgToCallExpr adds a testingT arg as the first arg of the +// ast.CallExpr and returns a copy of the ast.CallExpr +func addMissingTestingTArgToCallExpr(callExpr *ast.CallExpr, testingT ast.Expr) *ast.CallExpr { + return &ast.CallExpr{ + Fun: callExpr.Fun, + Args: append([]ast.Expr{removePos(testingT)}, callExpr.Args...), + } +} + +func removePos(node ast.Expr) ast.Expr { + switch typed := node.(type) { + case *ast.Ident: + return &ast.Ident{Name: typed.Name} + } + return node +} + +// TODO: use pkgInfo and walkForType instead? +func isAssignmentFromAssertNew(assign *ast.AssignStmt, migration migration) bool { + callExpr := callExprFromAssignment(assign) + if callExpr == nil { + return false + } + tcall, ok := newCallFromCallExpr(callExpr, migration) + if !ok { + return false + } + if !migration.importNames.matchesTestify(tcall.xIdent) { + return false + } + + if len(tcall.expr.Args) != 1 { + return false + } + return tcall.selExpr.Sel.Name == "New" +} + +func callExprFromAssignment(assign *ast.AssignStmt) *ast.CallExpr { + if len(assign.Rhs) != 1 { + return nil + } + + callExpr, ok := assign.Rhs[0].(*ast.CallExpr) + if !ok { + return nil + } + return callExpr +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call_test.go new file mode 100644 index 0000000..aaf7cc9 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/call_test.go @@ -0,0 +1,35 @@ +package main + +import ( + "go/ast" + "go/token" + "testing" + + "gotest.tools/v3/assert" +) + +func TestCall_String(t *testing.T) { + c := &call{ + expr: &ast.CallExpr{Fun: ast.NewIdent("myFunc")}, + } + assert.Equal(t, c.String(), "myFunc()") +} + +func TestCall_StringWithFileInfo(t *testing.T) { + c := &call{ + fileset: token.NewFileSet(), + expr: &ast.CallExpr{ + Fun: &ast.Ident{ + Name: "myFunc", + NamePos: 17, + }}, + } + t.Run("unknown file", func(t *testing.T) { + assert.Equal(t, c.StringWithFileInfo(), "myFunc() at unknown file") + }) + + t.Run("at position", func(t *testing.T) { + c.fileset.AddFile("source.go", 10, 100) + assert.Equal(t, c.StringWithFileInfo(), "myFunc() at source.go:1") + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/doc.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/doc.go new file mode 100644 index 0000000..da953bf --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/doc.go @@ -0,0 +1,22 @@ +/* + +Command gty-migrate-from-testify migrates packages from +testify/assert and testify/require to gotest.tools/v3/assert. + + $ go get gotest.tools/v3/assert/cmd/gty-migrate-from-testify + +Usage: + + gty-migrate-from-testify [OPTIONS] PACKAGE [PACKAGE...] + +See --help for full usage. + + +To run on all packages (including external test packages) use: + + go list \ + -f '{{.ImportPath}} {{if .XTestGoFiles}}{{"\n"}}{{.ImportPath}}_test{{end}}' \ + ./... | xargs gty-migrate-from-testify + +*/ +package main diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main.go new file mode 100644 index 0000000..e788654 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main.go @@ -0,0 +1,262 @@ +package main + +import ( + "bytes" + "errors" + "fmt" + "go/ast" + "go/format" + "go/token" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" + + "github.com/spf13/pflag" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/imports" +) + +type options struct { + pkgs []string + dryRun bool + debug bool + cmpImportName string + showLoaderErrors bool + buildFlags []string + localImportPath string +} + +func main() { + name := os.Args[0] + flags, opts := setupFlags(name) + handleExitError(name, flags.Parse(os.Args[1:])) + setupLogging(opts) + opts.pkgs = flags.Args() + handleExitError(name, run(*opts)) +} + +func setupLogging(opts *options) { + log.SetFlags(0) + enableDebug = opts.debug +} + +var enableDebug = false + +func debugf(msg string, args ...interface{}) { + if enableDebug { + log.Printf("DEBUG: "+msg, args...) + } +} + +func setupFlags(name string) (*pflag.FlagSet, *options) { + opts := options{} + flags := pflag.NewFlagSet(name, pflag.ContinueOnError) + flags.BoolVar(&opts.dryRun, "dry-run", false, + "don't write changes to file") + flags.BoolVar(&opts.debug, "debug", false, "enable debug logging") + flags.StringVar(&opts.cmpImportName, "cmp-pkg-import-alias", "is", + "import alias to use for the assert/cmp package") + flags.BoolVar(&opts.showLoaderErrors, "print-loader-errors", false, + "print errors from loading source") + flags.StringSliceVar(&opts.buildFlags, "build-tags", nil, + "build to pass to Go when loading source files") + flags.StringVar(&opts.localImportPath, "local-import-path", "", + "value to pass to 'goimports -local' flag for sorting local imports") + flags.Usage = func() { + fmt.Fprintf(os.Stderr, `Usage: %s [OPTIONS] PACKAGE [PACKAGE...] + +Migrate calls from testify/{assert|require} to gotest.tools/v3/assert. + +%s`, name, flags.FlagUsages()) + } + return flags, &opts +} + +func handleExitError(name string, err error) { + switch { + case err == nil: + return + case errors.Is(err, pflag.ErrHelp): + os.Exit(0) + default: + log.Println(name + ": Error: " + err.Error()) + os.Exit(3) + } +} + +func run(opts options) error { + imports.LocalPrefix = opts.localImportPath + + fset := token.NewFileSet() + pkgs, err := loadPackages(opts, fset) + if err != nil { + return fmt.Errorf("failed to load program: %w", err) + } + + debugf("package count: %d", len(pkgs)) + for _, pkg := range pkgs { + debugf("file count for package %v: %d", pkg.PkgPath, len(pkg.Syntax)) + for _, astFile := range pkg.Syntax { + absFilename := fset.File(astFile.Pos()).Name() + filename := relativePath(absFilename) + importNames := newImportNames(astFile.Imports, opts) + if !importNames.hasTestifyImports() { + debugf("skipping file %s, no imports", filename) + continue + } + + debugf("migrating %s with imports: %#v", filename, importNames) + m := migration{ + file: astFile, + fileset: fset, + importNames: importNames, + pkgInfo: pkg.TypesInfo, + } + migrateFile(m) + if opts.dryRun { + continue + } + + raw, err := formatFile(m) + if err != nil { + return fmt.Errorf("failed to format %s: %w", filename, err) + } + + if err := ioutil.WriteFile(absFilename, raw, 0); err != nil { + return fmt.Errorf("failed to write file %s: %w", filename, err) + } + } + } + + return nil +} + +var loadMode = packages.NeedName | + packages.NeedFiles | + packages.NeedCompiledGoFiles | + packages.NeedDeps | + packages.NeedImports | + packages.NeedTypes | + packages.NeedTypesInfo | + packages.NeedTypesSizes | + packages.NeedSyntax + +func loadPackages(opts options, fset *token.FileSet) ([]*packages.Package, error) { + conf := &packages.Config{ + Mode: loadMode, + Fset: fset, + Tests: true, + Logf: debugf, + BuildFlags: opts.buildFlags, + } + + pkgs, err := packages.Load(conf, opts.pkgs...) + if err != nil { + return nil, err + } + if opts.showLoaderErrors { + packages.PrintErrors(pkgs) + } + return pkgs, nil +} + +func relativePath(p string) string { + cwd, err := os.Getwd() + if err != nil { + return p + } + rel, err := filepath.Rel(cwd, p) + if err != nil { + return p + } + return rel +} + +type importNames struct { + testifyAssert string + testifyRequire string + assert string + cmp string +} + +func (p importNames) hasTestifyImports() bool { + return p.testifyAssert != "" || p.testifyRequire != "" +} + +func (p importNames) matchesTestify(ident *ast.Ident) bool { + return ident.Name == p.testifyAssert || ident.Name == p.testifyRequire +} + +func (p importNames) funcNameFromTestifyName(name string) string { + switch name { + case p.testifyAssert: + return funcNameCheck + case p.testifyRequire: + return funcNameAssert + default: + panic("unexpected testify import name " + name) + } +} + +func newImportNames(imports []*ast.ImportSpec, opt options) importNames { + defaultAssertAlias := path.Base(pkgAssert) + importNames := importNames{ + assert: defaultAssertAlias, + cmp: path.Base(pkgCmp), + } + for _, spec := range imports { + switch strings.Trim(spec.Path.Value, `"`) { + case pkgTestifyAssert, pkgGopkgTestifyAssert: + importNames.testifyAssert = identOrDefault(spec.Name, "assert") + case pkgTestifyRequire, pkgGopkgTestifyRequire: + importNames.testifyRequire = identOrDefault(spec.Name, "require") + default: + pkgPath := strings.Trim(spec.Path.Value, `"`) + + switch { + // v3/assert is already imported and has an alias + case pkgPath == pkgAssert: + if spec.Name != nil && spec.Name.Name != "" { + importNames.assert = spec.Name.Name + } + continue + + // some other package is imported as assert + case importedAs(spec, path.Base(pkgAssert)) && importNames.assert == defaultAssertAlias: + importNames.assert = "gtyassert" + } + } + } + + if opt.cmpImportName != "" { + importNames.cmp = opt.cmpImportName + } + return importNames +} + +func importedAs(spec *ast.ImportSpec, pkg string) bool { + if path.Base(strings.Trim(spec.Path.Value, `"`)) == pkg { + return true + } + return spec.Name != nil && spec.Name.Name == pkg +} + +func identOrDefault(ident *ast.Ident, def string) string { + if ident != nil { + return ident.Name + } + return def +} + +func formatFile(migration migration) ([]byte, error) { + buf := new(bytes.Buffer) + err := format.Node(buf, migration.fileset, migration.file) + if err != nil { + return nil, err + } + filename := migration.fileset.File(migration.file.Pos()).Name() + return imports.Process(filename, buf.Bytes(), nil) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main_test.go new file mode 100644 index 0000000..e5accbb --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/main_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "io/ioutil" + "testing" + + "github.com/google/go-cmp/cmp" + "gotest.tools/v3/assert" + "gotest.tools/v3/env" + "gotest.tools/v3/fs" + "gotest.tools/v3/golden" +) + +func TestRun(t *testing.T) { + setupLogging(&options{}) + dir := fs.NewDir(t, "test-run", + fs.WithDir("src/example.com/example", fs.FromDir("testdata/full"))) + defer dir.Remove() + + defer env.Patch(t, "GO111MODULE", "off")() + defer env.Patch(t, "GOPATH", dir.Path())() + err := run(options{ + pkgs: []string{"example.com/example"}, + showLoaderErrors: true, + }) + assert.NilError(t, err) + + raw, err := ioutil.ReadFile(dir.Join("src/example.com/example/some_test.go")) + assert.NilError(t, err) + golden.Assert(t, string(raw), "full-expected/some_test.go") +} + +func TestSetupFlags(t *testing.T) { + flags, opts := setupFlags("testing") + assert.Assert(t, flags.Usage != nil) + + err := flags.Parse([]string{ + "--dry-run", + "--debug", + "--cmp-pkg-import-alias=foo", + "--print-loader-errors", + }) + assert.NilError(t, err) + expected := &options{ + dryRun: true, + debug: true, + cmpImportName: "foo", + showLoaderErrors: true, + } + assert.DeepEqual(t, opts, expected, cmpOptions) +} + +var cmpOptions = cmp.AllowUnexported(options{}) diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate.go new file mode 100644 index 0000000..42dbe5e --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate.go @@ -0,0 +1,389 @@ +package main + +import ( + "go/ast" + "go/token" + "go/types" + "log" + "path" + + "golang.org/x/tools/go/ast/astutil" +) + +const ( + pkgTestifyAssert = "github.com/stretchr/testify/assert" + pkgGopkgTestifyAssert = "gopkg.in/stretchr/testify.v1/assert" + pkgTestifyRequire = "github.com/stretchr/testify/require" + pkgGopkgTestifyRequire = "gopkg.in/stretchr/testify.v1/require" + pkgAssert = "gotest.tools/v3/assert" + pkgCmp = "gotest.tools/v3/assert/cmp" +) + +const ( + funcNameAssert = "Assert" + funcNameCheck = "Check" +) + +var allTestifyPks = []string{ + pkgTestifyAssert, + pkgTestifyRequire, + pkgGopkgTestifyAssert, + pkgGopkgTestifyRequire, +} + +type migration struct { + file *ast.File + fileset *token.FileSet + importNames importNames + pkgInfo *types.Info +} + +func migrateFile(migration migration) { + astutil.Apply(migration.file, nil, replaceCalls(migration)) + updateImports(migration) +} + +func updateImports(migration migration) { + for _, remove := range allTestifyPks { + astutil.DeleteImport(migration.fileset, migration.file, remove) + } + + var alias string + if migration.importNames.assert != path.Base(pkgAssert) { + alias = migration.importNames.assert + } + astutil.AddNamedImport(migration.fileset, migration.file, alias, pkgAssert) + + if migration.importNames.cmp != path.Base(pkgCmp) { + alias = migration.importNames.cmp + } + astutil.AddNamedImport(migration.fileset, migration.file, alias, pkgCmp) +} + +type emptyNode struct{} + +func (n emptyNode) Pos() token.Pos { + return 0 +} + +func (n emptyNode) End() token.Pos { + return 0 +} + +var removeNode = emptyNode{} + +func replaceCalls(migration migration) func(cursor *astutil.Cursor) bool { + return func(cursor *astutil.Cursor) bool { + var newNode ast.Node + switch typed := cursor.Node().(type) { + case *ast.SelectorExpr: + newNode = getReplacementTestingT(typed, migration.importNames) + case *ast.CallExpr: + newNode = getReplacementAssertion(typed, migration) + case *ast.AssignStmt: + newNode = getReplacementAssignment(typed, migration) + } + + switch newNode { + case nil: + case removeNode: + cursor.Delete() + default: + cursor.Replace(newNode) + } + return true + } +} + +func getReplacementTestingT(selector *ast.SelectorExpr, names importNames) ast.Node { + xIdent, ok := selector.X.(*ast.Ident) + if !ok { + return nil + } + if selector.Sel.Name != "TestingT" || !names.matchesTestify(xIdent) { + return nil + } + return &ast.SelectorExpr{ + X: &ast.Ident{Name: names.assert, NamePos: xIdent.NamePos}, + Sel: selector.Sel, + } +} + +func getReplacementAssertion(callExpr *ast.CallExpr, migration migration) ast.Node { + tcall, ok := newTestifyCallFromNode(callExpr, migration) + if !ok { + return nil + } + if len(tcall.expr.Args) < 2 { + return convertTestifySingleArgCall(tcall) + } + return convertTestifyAssertion(tcall, migration) +} + +func getReplacementAssignment(assign *ast.AssignStmt, migration migration) ast.Node { + if isAssignmentFromAssertNew(assign, migration) { + return removeNode + } + return nil +} + +func convertTestifySingleArgCall(tcall call) ast.Node { + switch tcall.selExpr.Sel.Name { + case "TestingT": + // handled as SelectorExpr + return nil + case "New": + // handled by getReplacementAssignment + return nil + default: + log.Printf("%s: skipping unknown selector", tcall.StringWithFileInfo()) + return nil + } +} + +// nolint: maintidx +func convertTestifyAssertion(tcall call, migration migration) ast.Node { + imports := migration.importNames + + switch tcall.selExpr.Sel.Name { + case "NoError", "NoErrorf": + return convertNoError(tcall, imports) + case "True", "Truef": + return convertTrue(tcall, imports) + case "False", "Falsef": + return convertFalse(tcall, imports) + case "Equal", "Equalf", "Exactly", "Exactlyf", "EqualValues", "EqualValuesf": + return convertEqual(tcall, migration) + case "Contains", "Containsf": + return convertTwoArgComparison(tcall, imports, "Contains") + case "Len", "Lenf": + return convertTwoArgComparison(tcall, imports, "Len") + case "Panics", "Panicsf": + return convertOneArgComparison(tcall, imports, "Panics") + case "EqualError", "EqualErrorf": + return convertEqualError(tcall, imports) + case "Error", "Errorf": + return convertError(tcall, imports) + case "ErrorContains", "ErrorContainsf": + return convertErrorContains(tcall, imports) + case "Empty", "Emptyf": + return convertEmpty(tcall, imports) + case "Nil", "Nilf": + return convertNil(tcall, migration) + case "NotNil", "NotNilf": + return convertNegativeComparison(tcall, imports, &ast.Ident{Name: "nil"}, 2) + case "NotEqual", "NotEqualf": + return convertNegativeComparison(tcall, imports, tcall.arg(2), 3) + case "Fail", "Failf": + return convertFail(tcall, "Error") + case "FailNow", "FailNowf": + return convertFail(tcall, "Fatal") + case "NotEmpty", "NotEmptyf": + return convertNotEmpty(tcall, imports) + case "NotZero", "NotZerof": + zero := &ast.BasicLit{Kind: token.INT, Value: "0"} + return convertNegativeComparison(tcall, imports, zero, 2) + } + log.Printf("%s: skipping unsupported assertion", tcall.StringWithFileInfo()) + return nil +} + +func newCallExpr(x, sel string, args []ast.Expr) *ast.CallExpr { + return &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{Name: x}, + Sel: &ast.Ident{Name: sel}, + }, + Args: args, + } +} + +func newCallExprArgs(t ast.Expr, cmp ast.Expr, extra ...ast.Expr) []ast.Expr { + return append(append([]ast.Expr{t}, cmp), extra...) +} + +func newCallExprWithPosition(tcall call, imports importNames, args []ast.Expr) *ast.CallExpr { + return &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: imports.assert, + NamePos: tcall.xIdent.NamePos, + }, + Sel: &ast.Ident{Name: tcall.assert}, + }, + Args: args, + } +} + +func convertNoError(tcall call, imports importNames) ast.Node { + // use assert.NilError() for require.NoError() + if tcall.assert == funcNameAssert { + return newCallExprWithoutComparison(tcall, imports, "NilError") + } + // use assert.Check() for assert.NoError() + return newCallExprWithoutComparison(tcall, imports, "Check") +} + +func convertEqualError(tcall call, imports importNames) ast.Node { + if tcall.assert == funcNameAssert { + return newCallExprWithoutComparison(tcall, imports, "Error") + } + return convertTwoArgComparison(tcall, imports, "Error") +} + +func newCallExprWithoutComparison(tcall call, imports importNames, name string) ast.Node { + return &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: imports.assert, + NamePos: tcall.xIdent.NamePos, + }, + Sel: &ast.Ident{Name: name}, + }, + Args: tcall.expr.Args, + } +} + +func convertOneArgComparison(tcall call, imports importNames, cmpName string) ast.Node { + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + newCallExpr(imports.cmp, cmpName, []ast.Expr{tcall.arg(1)}), + tcall.extraArgs(2)...)) +} + +func convertTrue(tcall call, imports importNames) ast.Node { + return newCallExprWithPosition(tcall, imports, tcall.expr.Args) +} + +func convertFalse(tcall call, imports importNames) ast.Node { + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + &ast.UnaryExpr{Op: token.NOT, X: tcall.arg(1)}, + tcall.extraArgs(2)...)) +} + +func convertEqual(tcall call, migration migration) ast.Node { + imports := migration.importNames + + hasExtraArgs := len(tcall.extraArgs(3)) > 0 + + cmpEqual := convertTwoArgComparison(tcall, imports, "Equal") + if tcall.assert == funcNameAssert { + cmpEqual = newCallExprWithoutComparison(tcall, imports, "Equal") + } + cmpDeepEqual := convertTwoArgComparison(tcall, imports, "DeepEqual") + if tcall.assert == funcNameAssert && !hasExtraArgs { + cmpDeepEqual = newCallExprWithoutComparison(tcall, imports, "DeepEqual") + } + + gotype := walkForType(migration.pkgInfo, tcall.arg(1)) + if isUnknownType(gotype) { + gotype = walkForType(migration.pkgInfo, tcall.arg(2)) + } + if isUnknownType(gotype) { + return cmpDeepEqual + } + + switch gotype.Underlying().(type) { + case *types.Basic: + return cmpEqual + default: + return cmpDeepEqual + } +} + +func convertTwoArgComparison(tcall call, imports importNames, cmpName string) ast.Node { + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + newCallExpr(imports.cmp, cmpName, tcall.args(1, 3)), + tcall.extraArgs(3)...)) +} + +func convertError(tcall call, imports importNames) ast.Node { + cmpArgs := []ast.Expr{ + tcall.arg(1), + &ast.BasicLit{Kind: token.STRING, Value: `""`}} + + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + newCallExpr(imports.cmp, "ErrorContains", cmpArgs), + tcall.extraArgs(2)...)) +} + +func convertErrorContains(tcall call, imports importNames) ast.Node { + return &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: imports.assert, + NamePos: tcall.xIdent.NamePos, + }, + Sel: &ast.Ident{Name: "ErrorContains"}, + }, + Args: tcall.expr.Args, + } +} + +func convertEmpty(tcall call, imports importNames) ast.Node { + cmpArgs := []ast.Expr{ + tcall.arg(1), + &ast.BasicLit{Kind: token.INT, Value: "0"}, + } + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + newCallExpr(imports.cmp, "Len", cmpArgs), + tcall.extraArgs(2)...)) +} + +func convertNil(tcall call, migration migration) ast.Node { + gotype := walkForType(migration.pkgInfo, tcall.arg(1)) + if gotype != nil && gotype.String() == "error" { + return convertNoError(tcall, migration.importNames) + } + return convertOneArgComparison(tcall, migration.importNames, "Nil") +} + +func convertNegativeComparison( + tcall call, + imports importNames, + right ast.Expr, + extra int, +) ast.Node { + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + &ast.BinaryExpr{X: tcall.arg(1), Op: token.NEQ, Y: right}, + tcall.extraArgs(extra)...)) +} + +func convertFail(tcall call, selector string) ast.Node { + extraArgs := tcall.extraArgs(1) + if len(extraArgs) > 1 { + selector += "f" + } + + return &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: tcall.testingT(), + Sel: &ast.Ident{Name: selector}, + }, + Args: extraArgs, + } +} + +func convertNotEmpty(tcall call, imports importNames) ast.Node { + lenExpr := &ast.CallExpr{ + Fun: &ast.Ident{Name: "len"}, + Args: tcall.args(1, 2), + } + zeroExpr := &ast.BasicLit{Kind: token.INT, Value: "0"} + return newCallExprWithPosition(tcall, imports, + newCallExprArgs( + tcall.testingT(), + &ast.BinaryExpr{X: lenExpr, Op: token.NEQ, Y: zeroExpr}, + tcall.extraArgs(2)...)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate_test.go new file mode 100644 index 0000000..a3ec56e --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/migrate_test.go @@ -0,0 +1,381 @@ +package main + +import ( + "go/token" + "testing" + + "golang.org/x/tools/go/packages" + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/env" + "gotest.tools/v3/fs" + "gotest.tools/v3/icmd" +) + +func TestMigrateFileReplacesTestingT(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSomething(t *testing.T) { + a := assert.TestingT(t) + b := require.TestingT(t) + c := require.TestingT(t) + if a == b {} + _ = c +} + +func do(t require.TestingT) {} +` + migration := newMigrationFromSource(t, source) + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestSomething(t *testing.T) { + a := assert.TestingT(t) + b := assert.TestingT(t) + c := assert.TestingT(t) + if a == b { + } + _ = c +} + +func do(t assert.TestingT) {} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func newMigrationFromSource(t *testing.T, source string) migration { + t.Helper() + goMod := `module example.com/foo + +require github.com/stretchr/testify v1.7.1 +` + + dir := fs.NewDir(t, t.Name(), + fs.WithFile("foo.go", source), + fs.WithFile("go.mod", goMod)) + fileset := token.NewFileSet() + + env.ChangeWorkingDir(t, dir.Path()) + icmd.RunCommand("go", "mod", "tidy").Assert(t, icmd.Success) + + opts := options{pkgs: []string{"./..."}} + pkgs, err := loadPackages(opts, fileset) + assert.NilError(t, err) + packages.PrintErrors(pkgs) + + pkg := pkgs[0] + assert.Assert(t, !pkg.IllTyped) + + return migration{ + file: pkg.Syntax[0], + fileset: fileset, + importNames: newImportNames(pkg.Syntax[0].Imports, opts), + pkgInfo: pkg.TypesInfo, + } +} + +func TestMigrateFileWithNamedCmpPackage(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestSomething(t *testing.T) { + assert.Equal(t, "a", "b") +} +` + migration := newMigrationFromSource(t, source) + migration.importNames.cmp = "is" + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestSomething(t *testing.T) { + assert.Check(t, is.Equal("a", "b")) +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func TestMigrateFileWithCommentsOnAssert(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestSomething(t *testing.T) { + // This is going to fail + assert.Equal(t, "a", "b") +} +` + migration := newMigrationFromSource(t, source) + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +func TestSomething(t *testing.T) { + // This is going to fail + assert.Check(t, cmp.Equal("a", "b")) +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func TestMigrateFileConvertNilToNilError(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" +) + +func TestSomething(t *testing.T) { + var err error + assert.Nil(t, err) + require.Nil(t, err) +} +` + migration := newMigrationFromSource(t, source) + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestSomething(t *testing.T) { + var err error + assert.Check(t, err) + assert.NilError(t, err) +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func TestMigrateFileConvertAssertNew(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSomething(t *testing.T) { + is := assert.New(t) + is.Equal("one", "two") + is.NotEqual("one", "two") + + assert := require.New(t) + assert.Equal("one", "two") + assert.NotEqual("one", "two") +} + +func TestOtherName(z *testing.T) { + is := require.New(z) + is.Equal("one", "two") +} + +` + migration := newMigrationFromSource(t, source) + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +func TestSomething(t *testing.T) { + + assert.Check(t, cmp.Equal("one", "two")) + assert.Check(t, "one" != "two") + + assert.Equal(t, "one", "two") + assert.Assert(t, "one" != "two") +} + +func TestOtherName(z *testing.T) { + + assert.Equal(z, "one", "two") +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func TestMigrateFileWithExtraArgs(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSomething(t *testing.T) { + var err error + assert.Error(t, err, "this is a comment") + require.ErrorContains(t, err, "this in the error") + assert.Empty(t, nil, "more comment") + require.Equal(t, []string{}, []string{}, "because") +} +` + migration := newMigrationFromSource(t, source) + migration.importNames.cmp = "is" + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestSomething(t *testing.T) { + var err error + assert.Check(t, is.ErrorContains(err, ""), "this is a comment") + assert.ErrorContains(t, err, "this in the error") + assert.Check(t, is.Len(nil, 0), "more comment") + assert.Assert(t, is.DeepEqual([]string{}, []string{}), "because") +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func TestMigrate_AssertAlreadyImported(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/require" + "gotest.tools/v3/assert" +) + +func TestSomething(t *testing.T) { + var err error + assert.Error(t, err, "this is the error") + require.Equal(t, []string{}, []string{}, "because") +} +` + migration := newMigrationFromSource(t, source) + migration.importNames.cmp = "is" + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestSomething(t *testing.T) { + var err error + assert.Error(t, err, "this is the error") + assert.Assert(t, is.DeepEqual([]string{}, []string{}), "because") +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} + +func TestMigrate_AssertAlreadyImportedWithAlias(t *testing.T) { + source := ` +package foo + +import ( + "testing" + "github.com/stretchr/testify/require" + gtya "gotest.tools/v3/assert" +) + +func TestSomething(t *testing.T) { + var err error + gtya.Error(t, err, "this is the error") + require.Equal(t, []string{}, []string{}, "because") +} +` + migration := newMigrationFromSource(t, source) + migration.importNames.cmp = "is" + migrateFile(migration) + + expected := `package foo + +import ( + "testing" + + gtya "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestSomething(t *testing.T) { + var err error + gtya.Error(t, err, "this is the error") + gtya.Assert(t, is.DeepEqual([]string{}, []string{}), "because") +} +` + actual, err := formatFile(migration) + assert.NilError(t, err) + assert.Assert(t, cmp.Equal(expected, string(actual))) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full-expected/some_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full-expected/some_test.go new file mode 100644 index 0000000..1cbdf0f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full-expected/some_test.go @@ -0,0 +1,150 @@ +package foo + +import ( + "fmt" + "testing" + + "github.com/go-check/check" + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +type mystruct struct { + a int + expected int +} + +func TestFirstThing(t *testing.T) { + rt := assert.TestingT(t) + assert.Check(t, cmp.Equal("foo", "bar")) + assert.Check(t, cmp.Equal(1, 2)) + assert.Check(t, false) + assert.Check(t, !true) + assert.NilError(rt, nil) + + assert.Check(t, cmp.DeepEqual(map[string]bool{"a": true}, nil)) + assert.Check(t, cmp.DeepEqual([]int{1}, nil)) + assert.Equal(rt, "a", "B") +} + +func TestSecondThing(t *testing.T) { + var foo mystruct + assert.DeepEqual(t, foo, mystruct{}) + + assert.DeepEqual(t, mystruct{}, mystruct{}) + + assert.Check(t, nil, "foo %d", 3) + assert.NilError(t, nil, "foo %d", 3) + + assert.Check(t, cmp.ErrorContains(fmt.Errorf("foo"), "")) + + assert.Assert(t, 77 != 0) +} + +func TestOthers(t *testing.T) { + assert.Check(t, cmp.Contains([]string{}, "foo")) + assert.Assert(t, cmp.Len([]int{}, 3)) + assert.Check(t, cmp.Panics(func() { panic("foo") })) + assert.Error(t, fmt.Errorf("bad days"), "good days") + assert.Check(t, nil != nil) + + t.Error("why") + t.Fatal("why not") + assert.Assert(t, len([]bool{}) != 0) + + // Unsupported asseert + assert.NotContains(t, []bool{}, true) +} + +func TestAssertNew(t *testing.T) { + + assert.Check(t, cmp.Equal("a", "b")) +} + +type unit struct { + c *testing.T +} + +func thing(t *testing.T) unit { + return unit{c: t} +} + +func TestStoredTestingT(t *testing.T) { + u := thing(t) + assert.Check(u.c, cmp.Equal("A", "b")) + + u = unit{c: t} + assert.Check(u.c, cmp.Equal("A", "b")) +} + +func TestNotNamedT(c *testing.T) { + assert.Check(c, cmp.Equal("A", "b")) +} + +func TestEqualsWithComplexTypes(t *testing.T) { + expected := []int{1, 2, 3} + assert.Check(t, cmp.DeepEqual(expected, nil)) + + expectedM := map[int]bool{} + assert.Check(t, cmp.DeepEqual(expectedM, nil)) + + expectedI := 123 + assert.Check(t, cmp.Equal(expectedI, 0)) + + assert.Check(t, cmp.Equal(doInt(), 3)) + // TODO: struct field +} + +func doInt() int { + return 1 +} + +func TestEqualWithPrimitiveTypes(t *testing.T) { + s := "foo" + ptrString := &s + assert.Check(t, cmp.Equal(*ptrString, "foo")) + + assert.Check(t, cmp.Equal(doInt(), doInt())) + + x := doInt() + y := doInt() + assert.Check(t, cmp.Equal(x, y)) + + tc := mystruct{a: 3, expected: 5} + assert.Check(t, cmp.Equal(tc.a, tc.expected)) +} + +func TestTableTest(t *testing.T) { + var testcases = []struct { + opts []string + actual string + expected string + expectedOpts []string + }{ + { + opts: []string{"a", "b"}, + actual: "foo", + expected: "else", + }, + } + + for _, testcase := range testcases { + assert.Check(t, cmp.Equal(testcase.actual, testcase.expected)) + assert.Check(t, cmp.DeepEqual(testcase.opts, testcase.expectedOpts)) + } +} + +func TestWithChecker(c *check.C) { + var err error + assert.Check(c, err) +} + +func HelperWithAssertTestingT(t assert.TestingT) { + var err error + assert.Check(t, err, "with assert.TestingT") +} + +func BenchmarkSomething(b *testing.B) { + var err error + assert.Check(b, err) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full/some_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full/some_test.go new file mode 100644 index 0000000..f510038 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/testdata/full/some_test.go @@ -0,0 +1,151 @@ +package foo + +import ( + "fmt" + "testing" + + "github.com/go-check/check" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mystruct struct { + a int + expected int +} + +func TestFirstThing(t *testing.T) { + rt := require.TestingT(t) + assert.Equal(t, "foo", "bar") + assert.Equal(t, 1, 2) + assert.True(t, false) + assert.False(t, true) + require.NoError(rt, nil) + + assert.Equal(t, map[string]bool{"a": true}, nil) + assert.Equal(t, []int{1}, nil) + require.Equal(rt, "a", "B") +} + +func TestSecondThing(t *testing.T) { + var foo mystruct + require.Equal(t, foo, mystruct{}) + + require.Equal(t, mystruct{}, mystruct{}) + + assert.NoError(t, nil, "foo %d", 3) + require.NoError(t, nil, "foo %d", 3) + + assert.Error(t, fmt.Errorf("foo")) + + require.NotZero(t, 77) +} + +func TestOthers(t *testing.T) { + assert.Contains(t, []string{}, "foo") + require.Len(t, []int{}, 3) + assert.Panics(t, func() { panic("foo") }) + require.EqualError(t, fmt.Errorf("bad days"), "good days") + assert.NotNil(t, nil) + + assert.Fail(t, "why") + assert.FailNow(t, "why not") + require.NotEmpty(t, []bool{}) + + // Unsupported asseert + assert.NotContains(t, []bool{}, true) +} + +func TestAssertNew(t *testing.T) { + a := assert.New(t) + + a.Equal("a", "b") +} + +type unit struct { + c *testing.T +} + +func thing(t *testing.T) unit { + return unit{c: t} +} + +func TestStoredTestingT(t *testing.T) { + u := thing(t) + assert.Equal(u.c, "A", "b") + + u = unit{c: t} + assert.Equal(u.c, "A", "b") +} + +func TestNotNamedT(c *testing.T) { + assert.Equal(c, "A", "b") +} + +func TestEqualsWithComplexTypes(t *testing.T) { + expected := []int{1, 2, 3} + assert.Equal(t, expected, nil) + + expectedM := map[int]bool{} + assert.Equal(t, expectedM, nil) + + expectedI := 123 + assert.Equal(t, expectedI, 0) + + assert.Equal(t, doInt(), 3) + // TODO: struct field +} + +func doInt() int { + return 1 +} + +func TestEqualWithPrimitiveTypes(t *testing.T) { + s := "foo" + ptrString := &s + assert.Equal(t, *ptrString, "foo") + + assert.Equal(t, doInt(), doInt()) + + x := doInt() + y := doInt() + assert.Equal(t, x, y) + + tc := mystruct{a: 3, expected: 5} + assert.Equal(t, tc.a, tc.expected) +} + +func TestTableTest(t *testing.T) { + var testcases = []struct { + opts []string + actual string + expected string + expectedOpts []string + }{ + { + opts: []string{"a", "b"}, + actual: "foo", + expected: "else", + }, + } + + for _, testcase := range testcases { + assert.Equal(t, testcase.actual, testcase.expected) + assert.Equal(t, testcase.opts, testcase.expectedOpts) + } +} + +func TestWithChecker(c *check.C) { + var err error + assert.NoError(c, err) +} + +func HelperWithAssertTestingT(t assert.TestingT) { + var err error + assert.NoError(t, err, "with assert.TestingT") +} + +func BenchmarkSomething(b *testing.B) { + var err error + assert.NoError(b, err) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/walktype.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/walktype.go new file mode 100644 index 0000000..423f0ca --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmd/gty-migrate-from-testify/walktype.go @@ -0,0 +1,31 @@ +package main + +import ( + "go/ast" + "go/types" +) + +// walkForType walks the AST tree and returns the type of the expression +func walkForType(pkgInfo *types.Info, node ast.Node) types.Type { + var result types.Type + + visit := func(node ast.Node) bool { + if expr, ok := node.(ast.Expr); ok { + if typeAndValue, ok := pkgInfo.Types[expr]; ok { + result = typeAndValue.Type + return false + } + } + return true + } + ast.Inspect(node, visit) + return result +} + +func isUnknownType(typ types.Type) bool { + if typ == nil { + return true + } + basic, ok := typ.(*types.Basic) + return ok && basic.Kind() == types.Invalid +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare.go new file mode 100644 index 0000000..78f76e4 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare.go @@ -0,0 +1,394 @@ +/*Package cmp provides Comparisons for Assert and Check*/ +package cmp // import "gotest.tools/v3/assert/cmp" + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "strings" + + "github.com/google/go-cmp/cmp" + "gotest.tools/v3/internal/format" +) + +// Comparison is a function which compares values and returns ResultSuccess if +// the actual value matches the expected value. If the values do not match the +// Result will contain a message about why it failed. +type Comparison func() Result + +// DeepEqual compares two values using google/go-cmp +// (https://godoc.org/github.com/google/go-cmp/cmp) +// and succeeds if the values are equal. +// +// The comparison can be customized using comparison Options. +// Package http://pkg.go.dev/gotest.tools/v3/assert/opt provides some additional +// commonly used Options. +func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { + return func() (result Result) { + defer func() { + if panicmsg, handled := handleCmpPanic(recover()); handled { + result = ResultFailure(panicmsg) + } + }() + diff := cmp.Diff(x, y, opts...) + if diff == "" { + return ResultSuccess + } + return multiLineDiffResult(diff, x, y) + } +} + +func handleCmpPanic(r interface{}) (string, bool) { + if r == nil { + return "", false + } + panicmsg, ok := r.(string) + if !ok { + panic(r) + } + switch { + case strings.HasPrefix(panicmsg, "cannot handle unexported field"): + return panicmsg, true + } + panic(r) +} + +func toResult(success bool, msg string) Result { + if success { + return ResultSuccess + } + return ResultFailure(msg) +} + +// RegexOrPattern may be either a *regexp.Regexp or a string that is a valid +// regexp pattern. +type RegexOrPattern interface{} + +// Regexp succeeds if value v matches regular expression re. +// +// Example: +// assert.Assert(t, cmp.Regexp("^[0-9a-f]{32}$", str)) +// r := regexp.MustCompile("^[0-9a-f]{32}$") +// assert.Assert(t, cmp.Regexp(r, str)) +func Regexp(re RegexOrPattern, v string) Comparison { + match := func(re *regexp.Regexp) Result { + return toResult( + re.MatchString(v), + fmt.Sprintf("value %q does not match regexp %q", v, re.String())) + } + + return func() Result { + switch regex := re.(type) { + case *regexp.Regexp: + return match(regex) + case string: + re, err := regexp.Compile(regex) + if err != nil { + return ResultFailure(err.Error()) + } + return match(re) + default: + return ResultFailure(fmt.Sprintf("invalid type %T for regex pattern", regex)) + } + } +} + +// Equal succeeds if x == y. See assert.Equal for full documentation. +func Equal(x, y interface{}) Comparison { + return func() Result { + switch { + case x == y: + return ResultSuccess + case isMultiLineStringCompare(x, y): + diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)}) + return multiLineDiffResult(diff, x, y) + } + return ResultFailureTemplate(` + {{- printf "%v" .Data.x}} ( + {{- with callArg 0 }}{{ formatNode . }} {{end -}} + {{- printf "%T" .Data.x -}} + ) != {{ printf "%v" .Data.y}} ( + {{- with callArg 1 }}{{ formatNode . }} {{end -}} + {{- printf "%T" .Data.y -}} + )`, + map[string]interface{}{"x": x, "y": y}) + } +} + +func isMultiLineStringCompare(x, y interface{}) bool { + strX, ok := x.(string) + if !ok { + return false + } + strY, ok := y.(string) + if !ok { + return false + } + return strings.Contains(strX, "\n") || strings.Contains(strY, "\n") +} + +func multiLineDiffResult(diff string, x, y interface{}) Result { + return ResultFailureTemplate(` +--- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}} ++++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}} +{{ .Data.diff }}`, + map[string]interface{}{"diff": diff, "x": x, "y": y}) +} + +// Len succeeds if the sequence has the expected length. +func Len(seq interface{}, expected int) Comparison { + return func() (result Result) { + defer func() { + if e := recover(); e != nil { + result = ResultFailure(fmt.Sprintf("type %T does not have a length", seq)) + } + }() + value := reflect.ValueOf(seq) + length := value.Len() + if length == expected { + return ResultSuccess + } + msg := fmt.Sprintf("expected %s (length %d) to have length %d", seq, length, expected) + return ResultFailure(msg) + } +} + +// Contains succeeds if item is in collection. Collection may be a string, map, +// slice, or array. +// +// If collection is a string, item must also be a string, and is compared using +// strings.Contains(). +// If collection is a Map, contains will succeed if item is a key in the map. +// If collection is a slice or array, item is compared to each item in the +// sequence using reflect.DeepEqual(). +func Contains(collection interface{}, item interface{}) Comparison { + return func() Result { + colValue := reflect.ValueOf(collection) + if !colValue.IsValid() { + return ResultFailure("nil does not contain items") + } + msg := fmt.Sprintf("%v does not contain %v", collection, item) + + itemValue := reflect.ValueOf(item) + switch colValue.Type().Kind() { + case reflect.String: + if itemValue.Type().Kind() != reflect.String { + return ResultFailure("string may only contain strings") + } + return toResult( + strings.Contains(colValue.String(), itemValue.String()), + fmt.Sprintf("string %q does not contain %q", collection, item)) + + case reflect.Map: + if itemValue.Type() != colValue.Type().Key() { + return ResultFailure(fmt.Sprintf( + "%v can not contain a %v key", colValue.Type(), itemValue.Type())) + } + return toResult(colValue.MapIndex(itemValue).IsValid(), msg) + + case reflect.Slice, reflect.Array: + for i := 0; i < colValue.Len(); i++ { + if reflect.DeepEqual(colValue.Index(i).Interface(), item) { + return ResultSuccess + } + } + return ResultFailure(msg) + default: + return ResultFailure(fmt.Sprintf("type %T does not contain items", collection)) + } + } +} + +// Panics succeeds if f() panics. +func Panics(f func()) Comparison { + return func() (result Result) { + defer func() { + if err := recover(); err != nil { + result = ResultSuccess + } + }() + f() + return ResultFailure("did not panic") + } +} + +// Error succeeds if err is a non-nil error, and the error message equals the +// expected message. +func Error(err error, message string) Comparison { + return func() Result { + switch { + case err == nil: + return ResultFailure("expected an error, got nil") + case err.Error() != message: + return ResultFailure(fmt.Sprintf( + "expected error %q, got %s", message, formatErrorMessage(err))) + } + return ResultSuccess + } +} + +// ErrorContains succeeds if err is a non-nil error, and the error message contains +// the expected substring. +func ErrorContains(err error, substring string) Comparison { + return func() Result { + switch { + case err == nil: + return ResultFailure("expected an error, got nil") + case !strings.Contains(err.Error(), substring): + return ResultFailure(fmt.Sprintf( + "expected error to contain %q, got %s", substring, formatErrorMessage(err))) + } + return ResultSuccess + } +} + +type causer interface { + Cause() error +} + +func formatErrorMessage(err error) string { + // nolint: errorlint // unwrapping is not appropriate here + if _, ok := err.(causer); ok { + return fmt.Sprintf("%q\n%+v", err, err) + } + // This error was not wrapped with github.com/pkg/errors + return fmt.Sprintf("%q", err) +} + +// Nil succeeds if obj is a nil interface, pointer, or function. +// +// Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices, +// maps, and channels. +func Nil(obj interface{}) Comparison { + msgFunc := func(value reflect.Value) string { + return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type()) + } + return isNil(obj, msgFunc) +} + +func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison { + return func() Result { + if obj == nil { + return ResultSuccess + } + value := reflect.ValueOf(obj) + kind := value.Type().Kind() + if kind >= reflect.Chan && kind <= reflect.Slice { + if value.IsNil() { + return ResultSuccess + } + return ResultFailure(msgFunc(value)) + } + + return ResultFailure(fmt.Sprintf("%v (type %s) can not be nil", value, value.Type())) + } +} + +// ErrorType succeeds if err is not nil and is of the expected type. +// +// Expected can be one of: +// func(error) bool +// Function should return true if the error is the expected type. +// type struct{}, type &struct{} +// A struct or a pointer to a struct. +// Fails if the error is not of the same type as expected. +// type &interface{} +// A pointer to an interface type. +// Fails if err does not implement the interface. +// reflect.Type +// Fails if err does not implement the reflect.Type +func ErrorType(err error, expected interface{}) Comparison { + return func() Result { + switch expectedType := expected.(type) { + case func(error) bool: + return cmpErrorTypeFunc(err, expectedType) + case reflect.Type: + if expectedType.Kind() == reflect.Interface { + return cmpErrorTypeImplementsType(err, expectedType) + } + return cmpErrorTypeEqualType(err, expectedType) + case nil: + return ResultFailure("invalid type for expected: nil") + } + + expectedType := reflect.TypeOf(expected) + switch { + case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType): + return cmpErrorTypeEqualType(err, expectedType) + case isPtrToInterface(expectedType): + return cmpErrorTypeImplementsType(err, expectedType.Elem()) + } + return ResultFailure(fmt.Sprintf("invalid type for expected: %T", expected)) + } +} + +func cmpErrorTypeFunc(err error, f func(error) bool) Result { + if f(err) { + return ResultSuccess + } + actual := "nil" + if err != nil { + actual = fmt.Sprintf("%s (%T)", err, err) + } + return ResultFailureTemplate(`error is {{ .Data.actual }} + {{- with callArg 1 }}, not {{ formatNode . }}{{end -}}`, + map[string]interface{}{"actual": actual}) +} + +func cmpErrorTypeEqualType(err error, expectedType reflect.Type) Result { + if err == nil { + return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType)) + } + errValue := reflect.ValueOf(err) + if errValue.Type() == expectedType { + return ResultSuccess + } + return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType)) +} + +func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result { + if err == nil { + return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType)) + } + errValue := reflect.ValueOf(err) + if errValue.Type().Implements(expectedType) { + return ResultSuccess + } + return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType)) +} + +func isPtrToInterface(typ reflect.Type) bool { + return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface +} + +func isPtrToStruct(typ reflect.Type) bool { + return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct +} + +var ( + stdlibErrorNewType = reflect.TypeOf(errors.New("")) + stdlibFmtErrorType = reflect.TypeOf(fmt.Errorf("%w", fmt.Errorf(""))) +) + +// ErrorIs succeeds if errors.Is(actual, expected) returns true. See +// https://golang.org/pkg/errors/#Is for accepted argument values. +func ErrorIs(actual error, expected error) Comparison { + return func() Result { + if errors.Is(actual, expected) { + return ResultSuccess + } + + // The type of stdlib errors is excluded because the type is not relevant + // in those cases. The type is only important when it is a user defined + // custom error type. + return ResultFailureTemplate(`error is + {{- if not .Data.a }} nil,{{ else }} + {{- printf " \"%v\"" .Data.a }} + {{- if notStdlibErrorType .Data.a }} ({{ printf "%T" .Data.a }}){{ end }}, + {{- end }} not {{ printf "\"%v\"" .Data.x }} ( + {{- with callArg 1 }}{{ formatNode . }}{{ end }} + {{- if notStdlibErrorType .Data.x }}{{ printf " %T" .Data.x }}{{ end }})`, + map[string]interface{}{"a": actual, "x": expected}) + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare_test.go new file mode 100644 index 0000000..e4546ea --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/compare_test.go @@ -0,0 +1,680 @@ +package cmp + +import ( + "errors" + "fmt" + "go/ast" + "io" + "os" + "reflect" + "regexp" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestDeepEqual(t *testing.T) { + t.Run("failure", func(t *testing.T) { + result := DeepEqual([]string{"a", "b"}, []string{"b", "a"})() + if result.Success() { + t.Errorf("expected failure") + } + + args := []ast.Expr{&ast.Ident{Name: "result"}, &ast.Ident{Name: "exp"}} + message := result.(templatedResult).FailureMessage(args) + expected := "\n--- result\n+++ exp\n" + if !strings.HasPrefix(message, expected) { + t.Errorf("expected prefix \n%q\ngot\n%q\n", expected, message) + } + }) + + t.Run("success", func(t *testing.T) { + actual := DeepEqual([]string{"a"}, []string{"a"})() + assertSuccess(t, actual) + }) +} + +type Stub struct { + unx int +} + +func TestDeepEqualWithUnexported(t *testing.T) { + result := DeepEqual(Stub{}, Stub{unx: 1})() + assertFailureHasPrefix(t, result, `cannot handle unexported field at {cmp.Stub}.unx:`) +} + +func TestRegexp(t *testing.T) { + var testcases = []struct { + name string + regex interface{} + value string + match bool + expErr string + }{ + { + name: "pattern string match", + regex: "^[0-9]+$", + value: "12123423456", + match: true, + }, + { + name: "simple pattern string no match", + regex: "bob", + value: "Probably", + expErr: `value "Probably" does not match regexp "bob"`, + }, + { + name: "pattern string no match", + regex: "^1", + value: "2123423456", + expErr: `value "2123423456" does not match regexp "^1"`, + }, + { + name: "regexp match", + regex: regexp.MustCompile("^d[0-9a-f]{8}$"), + value: "d1632beef", + match: true, + }, + { + name: "invalid regexp", + regex: "^1(", + value: "2", + expErr: "error parsing regexp: missing closing ): `^1(`", + }, + { + name: "invalid type", + regex: struct{}{}, + value: "some string", + expErr: "invalid type struct {} for regex pattern", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + res := Regexp(tc.regex, tc.value)() + if tc.match { + assertSuccess(t, res) + } else { + assertFailure(t, res, tc.expErr) + } + }) + } +} + +func TestLen(t *testing.T) { + var testcases = []struct { + seq interface{} + length int + expectedSuccess bool + expectedMessage string + }{ + { + seq: []string{"A", "b", "c"}, + length: 3, + expectedSuccess: true, + }, + { + seq: []string{"A", "b", "c"}, + length: 2, + expectedMessage: "expected [A b c] (length 3) to have length 2", + }, + { + seq: map[string]int{"a": 1, "b": 2}, + length: 2, + expectedSuccess: true, + }, + { + seq: [3]string{"a", "b", "c"}, + length: 3, + expectedSuccess: true, + }, + { + seq: "abcd", + length: 4, + expectedSuccess: true, + }, + { + seq: "abcd", + length: 3, + expectedMessage: "expected abcd (length 4) to have length 3", + }, + } + + for _, testcase := range testcases { + t.Run(fmt.Sprintf("%v len=%d", testcase.seq, testcase.length), func(t *testing.T) { + result := Len(testcase.seq, testcase.length)() + if testcase.expectedSuccess { + assertSuccess(t, result) + } else { + assertFailure(t, result, testcase.expectedMessage) + } + }) + } +} + +func TestPanics(t *testing.T) { + panicker := func() { + panic("AHHHHHHHHHHH") + } + + result := Panics(panicker)() + assertSuccess(t, result) + + result = Panics(func() {})() + assertFailure(t, result, "did not panic") +} + +type innerstub struct { + num int +} + +type stub struct { + stub innerstub + num int +} + +func TestDeepEqualEquivalenceToReflectDeepEqual(t *testing.T) { + var testcases = []struct { + left interface{} + right interface{} + }{ + {nil, nil}, + {7, 7}, + {false, false}, + {stub{innerstub{1}, 2}, stub{innerstub{1}, 2}}, + {[]int{1, 2, 3}, []int{1, 2, 3}}, + {[]byte(nil), []byte(nil)}, + {nil, []byte(nil)}, + {1, uint64(1)}, + {7, "7"}, + } + for _, testcase := range testcases { + expected := reflect.DeepEqual(testcase.left, testcase.right) + res := DeepEqual(testcase.left, testcase.right, cmpStub)() + if res.Success() != expected { + msg := res.(StringResult).FailureMessage() + t.Errorf("deepEqual(%v, %v) did not return %v (message %s)", + testcase.left, testcase.right, expected, msg) + } + } +} + +var cmpStub = cmp.AllowUnexported(stub{}, innerstub{}) + +func TestContains(t *testing.T) { + var testcases = []struct { + seq interface{} + item interface{} + expected bool + expectedMsg string + }{ + { + seq: error(nil), + item: 0, + expectedMsg: "nil does not contain items", + }, + { + seq: "abcdef", + item: "cde", + expected: true, + }, + { + seq: "abcdef", + item: "foo", + expectedMsg: `string "abcdef" does not contain "foo"`, + }, + { + seq: "abcdef", + item: 3, + expectedMsg: `string may only contain strings`, + }, + { + seq: map[rune]int{'a': 1, 'b': 2}, + item: 'b', + expected: true, + }, + { + seq: map[rune]int{'a': 1}, + item: 'c', + expectedMsg: "map[97:1] does not contain 99", + }, + { + seq: map[int]int{'a': 1, 'b': 2}, + item: 'b', + expectedMsg: "map[int]int can not contain a int32 key", + }, + { + seq: []interface{}{"a", 1, 'a', 1.0, true}, + item: 'a', + expected: true, + }, + { + seq: []interface{}{"a", 1, 'a', 1.0, true}, + item: 3, + expectedMsg: "[a 1 97 1 true] does not contain 3", + }, + { + seq: [3]byte{99, 10, 100}, + item: byte(99), + expected: true, + }, + { + seq: [3]byte{99, 10, 100}, + item: byte(98), + expectedMsg: "[99 10 100] does not contain 98", + }, + } + for _, testcase := range testcases { + name := fmt.Sprintf("%v in %v", testcase.item, testcase.seq) + t.Run(name, func(t *testing.T) { + result := Contains(testcase.seq, testcase.item)() + if testcase.expected { + assertSuccess(t, result) + } else { + assertFailure(t, result, testcase.expectedMsg) + } + }) + } +} + +func TestEqualMultiLine(t *testing.T) { + result := `abcd +1234 +aaaa +bbbb` + + exp := `abcd +1111 +aaaa +bbbb` + + expected := ` +--- result ++++ exp +@@ -1,4 +1,4 @@ + abcd +-1234 ++1111 + aaaa + bbbb +` + + args := []ast.Expr{&ast.Ident{Name: "result"}, &ast.Ident{Name: "exp"}} + res := Equal(result, exp)() + assertFailureTemplate(t, res, args, expected) +} + +func TestEqual_PointersNotEqual(t *testing.T) { + x := 123 + y := 123 + + res := Equal(&x, &y)() + args := []ast.Expr{&ast.Ident{Name: "x"}, &ast.Ident{Name: "y"}} + expected := fmt.Sprintf("%p (x *int) != %p (y *int)", &x, &y) + assertFailureTemplate(t, res, args, expected) +} + +// errorWithCause mimics the error formatting of github.com/pkg/errors +type errorWithCause struct { + msg string + cause error +} + +func (e errorWithCause) Error() string { + return fmt.Sprintf("%v with cause: %v", e.msg, e.cause) +} + +func (e errorWithCause) Cause() error { + return e.cause +} + +func (e errorWithCause) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", e.Cause()) + fmt.Fprint(s, "\nstack trace") + return + } + fallthrough + case 's': + io.WriteString(s, e.Error()) + case 'q': + fmt.Fprintf(s, "%q", e.Error()) + } +} + +func TestError(t *testing.T) { + result := Error(nil, "the error message")() + assertFailure(t, result, "expected an error, got nil") + + // A Wrapped error also includes the stack + result = Error(errorWithCause{cause: errors.New("other"), msg: "wrapped"}, "the error message")() + assertFailureHasPrefix(t, result, + `expected error "the error message", got "wrapped with cause: other" +other +stack trace`) + + msg := "the message" + result = Error(errors.New(msg), msg)() + assertSuccess(t, result) +} + +func TestErrorContains(t *testing.T) { + result := ErrorContains(nil, "the error message")() + assertFailure(t, result, "expected an error, got nil") + + result = ErrorContains(errors.New("other"), "the error")() + assertFailureHasPrefix(t, result, + `expected error to contain "the error", got "other"`) + + msg := "the full message" + result = ErrorContains(errors.New(msg), "full")() + assertSuccess(t, result) +} + +func TestNil(t *testing.T) { + result := Nil(nil)() + assertSuccess(t, result) + + var s *string + result = Nil(s)() + assertSuccess(t, result) + + var closer io.Closer + result = Nil(closer)() + assertSuccess(t, result) + + result = Nil("wrong")() + assertFailure(t, result, "wrong (type string) can not be nil") + + notnil := "notnil" + result = Nil(¬nil)() + assertFailure(t, result, "notnil (type *string) is not nil") + + result = Nil([]string{"a"})() + assertFailure(t, result, "[a] (type []string) is not nil") +} + +type testingT interface { + Errorf(msg string, args ...interface{}) +} + +type helperT interface { + Helper() +} + +func assertSuccess(t testingT, res Result) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !res.Success() { + msg := res.(StringResult).FailureMessage() + t.Errorf("expected success, but got failure with message %q", msg) + } +} + +func assertFailure(t testingT, res Result, expected string) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if res.Success() { + t.Errorf("expected failure") + } + message := res.(StringResult).FailureMessage() + if message != expected { + t.Errorf("expected \n%q\ngot\n%q\n", expected, message) + } +} + +func assertFailureHasPrefix(t testingT, res Result, prefix string) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if res.Success() { + t.Errorf("expected failure") + } + message := res.(StringResult).FailureMessage() + if !strings.HasPrefix(message, prefix) { + t.Errorf("expected \n%v\nto start with\n%v\n", message, prefix) + } +} + +func assertFailureTemplate(t testingT, res Result, args []ast.Expr, expected string) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if res.Success() { + t.Errorf("expected failure") + } + message := res.(templatedResult).FailureMessage(args) + if message != expected { + t.Errorf("expected \n%q\ngot\n%q\n", expected, message) + } +} + +type stubError struct{} + +func (s stubError) Error() string { + return "stub error" +} + +func isErrorOfTypeStub(err error) bool { + return reflect.TypeOf(err) == reflect.TypeOf(stubError{}) +} + +type notStubError struct{} + +func (s notStubError) Error() string { + return "not stub error" +} + +func isErrorOfTypeNotStub(err error) bool { + return reflect.TypeOf(err) == reflect.TypeOf(notStubError{}) +} + +type specialStubIface interface { + Special() +} + +type stubPtrError struct{} + +func (s *stubPtrError) Error() string { + return "stub ptr error" +} + +func TestErrorTypeWithNil(t *testing.T) { + var testcases = []struct { + name string + expType interface{} + expected string + }{ + { + name: "with struct", + expType: stubError{}, + expected: "error is nil, not cmp.stubError", + }, + { + name: "with pointer to struct", + expType: &stubPtrError{}, + expected: "error is nil, not *cmp.stubPtrError", + }, + { + name: "with interface", + expType: (*specialStubIface)(nil), + expected: "error is nil, not cmp.specialStubIface", + }, + { + name: "with reflect.Type", + expType: reflect.TypeOf(stubError{}), + expected: "error is nil, not cmp.stubError", + }, + } + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + result := ErrorType(nil, testcase.expType)() + assertFailure(t, result, testcase.expected) + }) + } +} + +func TestErrorTypeSuccess(t *testing.T) { + var testcases = []struct { + name string + expType interface{} + err error + }{ + { + name: "with function", + expType: isErrorOfTypeStub, + err: stubError{}, + }, + { + name: "with struct", + expType: stubError{}, + err: stubError{}, + }, + { + name: "with pointer to struct", + expType: &stubPtrError{}, + err: &stubPtrError{}, + }, + { + name: "with interface", + expType: (*error)(nil), + err: stubError{}, + }, + { + name: "with reflect.Type struct", + expType: reflect.TypeOf(stubError{}), + err: stubError{}, + }, + { + name: "with reflect.Type interface", + expType: reflect.TypeOf((*error)(nil)).Elem(), + err: stubError{}, + }, + } + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + result := ErrorType(testcase.err, testcase.expType)() + assertSuccess(t, result) + }) + } +} + +func TestErrorTypeFailure(t *testing.T) { + var testcases = []struct { + name string + expType interface{} + expected string + }{ + { + name: "with struct", + expType: notStubError{}, + expected: "error is stub error (cmp.stubError), not cmp.notStubError", + }, + { + name: "with pointer to struct", + expType: &stubPtrError{}, + expected: "error is stub error (cmp.stubError), not *cmp.stubPtrError", + }, + { + name: "with interface", + expType: (*specialStubIface)(nil), + expected: "error is stub error (cmp.stubError), not cmp.specialStubIface", + }, + { + name: "with reflect.Type struct", + expType: reflect.TypeOf(notStubError{}), + expected: "error is stub error (cmp.stubError), not cmp.notStubError", + }, + { + name: "with reflect.Type interface", + expType: reflect.TypeOf((*specialStubIface)(nil)).Elem(), + expected: "error is stub error (cmp.stubError), not cmp.specialStubIface", + }, + } + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + result := ErrorType(stubError{}, testcase.expType)() + assertFailure(t, result, testcase.expected) + }) + } +} + +func TestErrorTypeInvalid(t *testing.T) { + result := ErrorType(stubError{}, nil)() + assertFailure(t, result, "invalid type for expected: nil") + + result = ErrorType(stubError{}, "my type!")() + assertFailure(t, result, "invalid type for expected: string") +} + +func TestErrorTypeWithFunc(t *testing.T) { + result := ErrorType(nil, isErrorOfTypeStub)() + assertFailureTemplate(t, result, + []ast.Expr{nil, &ast.Ident{Name: "isErrorOfTypeStub"}}, + "error is nil, not isErrorOfTypeStub") + + result = ErrorType(stubError{}, isErrorOfTypeNotStub)() + assertFailureTemplate(t, result, + []ast.Expr{nil, &ast.Ident{Name: "isErrorOfTypeNotStub"}}, + "error is stub error (cmp.stubError), not isErrorOfTypeNotStub") +} + +func TestErrorIs(t *testing.T) { + t.Run("equal", func(t *testing.T) { + result := ErrorIs(stubError{}, stubError{})() + assertSuccess(t, result) + }) + t.Run("actual is nil, not stdlib error", func(t *testing.T) { + result := ErrorIs(nil, stubError{})() + args := []ast.Expr{ + &ast.Ident{Name: "err"}, + &ast.SelectorExpr{ + X: &ast.Ident{Name: "mypkg"}, + Sel: &ast.Ident{Name: "StubError"}, + }, + } + expected := `error is nil, not "stub error" (mypkg.StubError cmp.stubError)` + assertFailureTemplate(t, result, args, expected) + }) + t.Run("not equal, not stdlib error", func(t *testing.T) { + result := ErrorIs(notStubError{}, stubError{})() + args := []ast.Expr{ + &ast.Ident{Name: "err"}, + &ast.SelectorExpr{ + X: &ast.Ident{Name: "mypkg"}, + Sel: &ast.Ident{Name: "StubError"}, + }, + } + expected := `error is "not stub error" (cmp.notStubError), not "stub error" (mypkg.StubError cmp.stubError)` + assertFailureTemplate(t, result, args, expected) + }) + t.Run("actual is nil, stdlib error", func(t *testing.T) { + result := ErrorIs(nil, os.ErrClosed)() + args := []ast.Expr{ + &ast.Ident{Name: "err"}, + &ast.SelectorExpr{ + X: &ast.Ident{Name: "os"}, + Sel: &ast.Ident{Name: "ErrClosed"}, + }, + } + expected := `error is nil, not "file already closed" (os.ErrClosed)` + assertFailureTemplate(t, result, args, expected) + }) + t.Run("not equal, stdlib error", func(t *testing.T) { + result := ErrorIs(fmt.Errorf("foo"), os.ErrClosed)() + args := []ast.Expr{ + &ast.Ident{Name: "err"}, + &ast.SelectorExpr{ + X: &ast.Ident{Name: "os"}, + Sel: &ast.Ident{Name: "ErrClosed"}, + }, + } + expected := `error is "foo", not "file already closed" (os.ErrClosed)` + assertFailureTemplate(t, result, args, expected) + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/result.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/result.go new file mode 100644 index 0000000..28ef8d3 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/cmp/result.go @@ -0,0 +1,110 @@ +package cmp + +import ( + "bytes" + "fmt" + "go/ast" + "reflect" + "text/template" + + "gotest.tools/v3/internal/source" +) + +// A Result of a Comparison. +type Result interface { + Success() bool +} + +// StringResult is an implementation of Result that reports the error message +// string verbatim and does not provide any templating or formatting of the +// message. +type StringResult struct { + success bool + message string +} + +// Success returns true if the comparison was successful. +func (r StringResult) Success() bool { + return r.success +} + +// FailureMessage returns the message used to provide additional information +// about the failure. +func (r StringResult) FailureMessage() string { + return r.message +} + +// ResultSuccess is a constant which is returned by a ComparisonWithResult to +// indicate success. +var ResultSuccess = StringResult{success: true} + +// ResultFailure returns a failed Result with a failure message. +func ResultFailure(message string) StringResult { + return StringResult{message: message} +} + +// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure +// is returned with the error message as the failure message. +func ResultFromError(err error) Result { + if err == nil { + return ResultSuccess + } + return ResultFailure(err.Error()) +} + +type templatedResult struct { + template string + data map[string]interface{} +} + +func (r templatedResult) Success() bool { + return false +} + +func (r templatedResult) FailureMessage(args []ast.Expr) string { + msg, err := renderMessage(r, args) + if err != nil { + return fmt.Sprintf("failed to render failure message: %s", err) + } + return msg +} + +func (r templatedResult) UpdatedExpected(stackIndex int) error { + // TODO: would be nice to have structured data instead of a map + return source.UpdateExpectedValue(stackIndex+1, r.data["x"], r.data["y"]) +} + +// ResultFailureTemplate returns a Result with a template string and data which +// can be used to format a failure message. The template may access data from .Data, +// the comparison args with the callArg function, and the formatNode function may +// be used to format the call args. +func ResultFailureTemplate(template string, data map[string]interface{}) Result { + return templatedResult{template: template, data: data} +} + +func renderMessage(result templatedResult, args []ast.Expr) (string, error) { + tmpl := template.New("failure").Funcs(template.FuncMap{ + "formatNode": source.FormatNode, + "callArg": func(index int) ast.Expr { + if index >= len(args) { + return nil + } + return args[index] + }, + // TODO: any way to include this from ErrorIS instead of here? + "notStdlibErrorType": func(typ interface{}) bool { + r := reflect.TypeOf(typ) + return r != stdlibFmtErrorType && r != stdlibErrorNewType + }, + }) + var err error + tmpl, err = tmpl.Parse(result.template) + if err != nil { + return "", err + } + buf := new(bytes.Buffer) + err = tmpl.Execute(buf, map[string]interface{}{ + "Data": result.data, + }) + return buf.String(), err +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/example_test.go new file mode 100644 index 0000000..2abfe12 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/example_test.go @@ -0,0 +1,26 @@ +package assert_test + +import ( + "fmt" + "regexp" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +var t = &testing.T{} + +func ExampleAssert_customComparison() { + regexPattern := func(value string, pattern string) cmp.Comparison { + return func() cmp.Result { + re := regexp.MustCompile(pattern) + if re.MatchString(value) { + return cmp.ResultSuccess + } + return cmp.ResultFailure( + fmt.Sprintf("%q did not match pattern %q", value, pattern)) + } + } + assert.Assert(t, regexPattern("12345.34", `\d+.\d\d`)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt.go new file mode 100644 index 0000000..357cdf2 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt.go @@ -0,0 +1,118 @@ +/*Package opt provides common go-cmp.Options for use with assert.DeepEqual. + */ +package opt // import "gotest.tools/v3/assert/opt" + +import ( + "fmt" + "reflect" + "strings" + "time" + + gocmp "github.com/google/go-cmp/cmp" +) + +// DurationWithThreshold returns a gocmp.Comparer for comparing time.Duration. The +// Comparer returns true if the difference between the two Duration values is +// within the threshold and neither value is zero. +func DurationWithThreshold(threshold time.Duration) gocmp.Option { + return gocmp.Comparer(cmpDuration(threshold)) +} + +func cmpDuration(threshold time.Duration) func(x, y time.Duration) bool { + return func(x, y time.Duration) bool { + if x == 0 || y == 0 { + return false + } + delta := x - y + return delta <= threshold && delta >= -threshold + } +} + +// TimeWithThreshold returns a gocmp.Comparer for comparing time.Time. The +// Comparer returns true if the difference between the two Time values is +// within the threshold and neither value is zero. +func TimeWithThreshold(threshold time.Duration) gocmp.Option { + return gocmp.Comparer(cmpTime(threshold)) +} + +func cmpTime(threshold time.Duration) func(x, y time.Time) bool { + return func(x, y time.Time) bool { + if x.IsZero() || y.IsZero() { + return false + } + delta := x.Sub(y) + return delta <= threshold && delta >= -threshold + } +} + +// PathString is a gocmp.FilterPath filter that returns true when path.String() +// matches any of the specs. +// +// The path spec is a dot separated string where each segment is a field name. +// Slices, Arrays, and Maps are always matched against every element in the +// sequence. gocmp.Indirect, gocmp.Transform, and gocmp.TypeAssertion are always +// ignored. +// +// Note: this path filter is not type safe. Incorrect paths will be silently +// ignored. Consider using a type safe path filter for more complex paths. +func PathString(specs ...string) func(path gocmp.Path) bool { + return func(path gocmp.Path) bool { + for _, spec := range specs { + if path.String() == spec { + return true + } + } + return false + } +} + +// PathDebug is a gocmp.FilerPath filter that always returns false. It prints +// each path it receives. It can be used to debug path matching problems. +func PathDebug(path gocmp.Path) bool { + fmt.Printf("PATH string=%s gostring=%s\n", path, path.GoString()) + for _, step := range path { + fmt.Printf(" STEP %s\ttype=%s\t%s\n", + formatStepType(step), step.Type(), stepTypeFields(step)) + } + return false +} + +func formatStepType(step gocmp.PathStep) string { + return strings.Title(strings.TrimPrefix(reflect.TypeOf(step).String(), "*cmp.")) +} + +func stepTypeFields(step gocmp.PathStep) string { + switch typed := step.(type) { + case gocmp.StructField: + return fmt.Sprintf("name=%s", typed.Name()) + case gocmp.MapIndex: + return fmt.Sprintf("key=%s", typed.Key().Interface()) + case gocmp.Transform: + return fmt.Sprintf("name=%s", typed.Name()) + case gocmp.SliceIndex: + return fmt.Sprintf("name=%d", typed.Key()) + } + return "" +} + +// PathField is a gocmp.FilerPath filter that matches a struct field by name. +// PathField will match every instance of the field in a recursive or nested +// structure. +func PathField(structType interface{}, field string) func(gocmp.Path) bool { + typ := reflect.TypeOf(structType) + if typ.Kind() != reflect.Struct { + panic(fmt.Sprintf("type %s is not a struct", typ)) + } + if _, ok := typ.FieldByName(field); !ok { + panic(fmt.Sprintf("type %s does not have field %s", typ, field)) + } + + return func(path gocmp.Path) bool { + return path.Index(-2).Type() == typ && isStructField(path.Index(-1), field) + } +} + +func isStructField(step gocmp.PathStep, name string) bool { + field, ok := step.(gocmp.StructField) + return ok && field.Name() == name +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt_test.go new file mode 100644 index 0000000..c036d18 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/assert/opt/opt_test.go @@ -0,0 +1,265 @@ +package opt + +import ( + "sort" + "testing" + "time" + + gocmp "github.com/google/go-cmp/cmp" + "gotest.tools/v3/assert" +) + +func TestDurationWithThreshold(t *testing.T) { + var testcases = []struct { + name string + x, y, threshold time.Duration + expected bool + }{ + { + name: "delta is threshold", + threshold: time.Second, + x: 3 * time.Second, + y: 2 * time.Second, + expected: true, + }, + { + name: "delta is negative threshold", + threshold: time.Second, + x: 2 * time.Second, + y: 3 * time.Second, + expected: true, + }, + { + name: "delta within threshold", + threshold: time.Second, + x: 300 * time.Millisecond, + y: 100 * time.Millisecond, + expected: true, + }, + { + name: "delta within negative threshold", + threshold: time.Second, + x: 100 * time.Millisecond, + y: 300 * time.Millisecond, + expected: true, + }, + { + name: "delta outside threshold", + threshold: time.Second, + x: 5 * time.Second, + y: 300 * time.Millisecond, + }, + { + name: "delta outside negative threshold", + threshold: time.Second, + x: 300 * time.Millisecond, + y: 5 * time.Second, + }, + { + name: "x is 0", + threshold: time.Second, + y: 5 * time.Millisecond, + }, + { + name: "y is 0", + threshold: time.Second, + x: 5 * time.Millisecond, + }, + } + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + actual := cmpDuration(testcase.threshold)(testcase.x, testcase.y) + assert.Equal(t, actual, testcase.expected) + }) + } +} + +func TestTimeWithThreshold(t *testing.T) { + var now = time.Now() + + var testcases = []struct { + name string + x, y time.Time + threshold time.Duration + expected bool + }{ + { + name: "delta is threshold", + threshold: time.Minute, + x: now, + y: now.Add(time.Minute), + expected: true, + }, + { + name: "delta is negative threshold", + threshold: time.Minute, + x: now, + y: now.Add(-time.Minute), + expected: true, + }, + { + name: "delta within threshold", + threshold: time.Hour, + x: now, + y: now.Add(time.Minute), + expected: true, + }, + { + name: "delta within negative threshold", + threshold: time.Hour, + x: now, + y: now.Add(-time.Minute), + expected: true, + }, + { + name: "delta outside threshold", + threshold: time.Second, + x: now, + y: now.Add(time.Minute), + }, + { + name: "delta outside negative threshold", + threshold: time.Second, + x: now, + y: now.Add(-time.Minute), + }, + { + name: "x is 0", + threshold: time.Second, + y: now, + }, + { + name: "y is 0", + threshold: time.Second, + x: now, + }, + } + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + actual := cmpTime(testcase.threshold)(testcase.x, testcase.y) + assert.Equal(t, actual, testcase.expected) + }) + } +} + +type node struct { + Value nodeValue + Labels map[string]node + Children []node + Ref *node +} + +type nodeValue struct { + Value int +} + +type pathRecorder struct { + filter func(p gocmp.Path) bool + matches []string +} + +func (p *pathRecorder) record(path gocmp.Path) bool { + if p.filter(path) { + p.matches = append(p.matches, path.GoString()) + } + return false +} + +func matchPaths(fixture interface{}, filter func(gocmp.Path) bool) []string { + rec := &pathRecorder{filter: filter} + gocmp.Equal(fixture, fixture, gocmp.FilterPath(rec.record, gocmp.Ignore())) + sort.Strings(rec.matches) + return rec.matches +} + +func TestPathStringFromStruct(t *testing.T) { + fixture := node{ + Ref: &node{ + Children: []node{ + {}, + { + Labels: map[string]node{ + "first": {Value: nodeValue{Value: 3}}, + }, + }, + }, + }, + } + + spec := "Ref.Children.Labels.Value" + matches := matchPaths(fixture, PathString(spec)) + expected := []string{ + `{opt.node}.Ref.Children[1].Labels["first"].Value`, + `{opt.node}.Ref.Children[1].Labels["first"].Value`, + } + assert.DeepEqual(t, matches, expected) +} + +func TestPathStringFromSlice(t *testing.T) { + fixture := []node{ + { + Ref: &node{ + Children: []node{ + {}, + { + Labels: map[string]node{ + "first": {}, + "second": { + Ref: &node{Value: nodeValue{Value: 3}}, + }, + }, + }, + }, + }, + }, + } + + spec := "Ref.Children.Labels.Ref.Value" + matches := matchPaths(fixture, PathString(spec)) + expected := []string{ + `{[]opt.node}[0].Ref.Children[1].Labels["second"].Ref.Value`, + `{[]opt.node}[0].Ref.Children[1].Labels["second"].Ref.Value`, + `{[]opt.node}[0].Ref.Children[1].Labels["second"].Ref.Value`, + `{[]opt.node}[0].Ref.Children[1].Labels["second"].Ref.Value`, + } + assert.DeepEqual(t, matches, expected) +} + +func TestPathField(t *testing.T) { + fixture := node{ + Value: nodeValue{Value: 3}, + Children: []node{ + {}, + {Value: nodeValue{Value: 2}}, + {Ref: &node{Value: nodeValue{Value: 9}}}, + }, + } + + filter := PathField(nodeValue{}, "Value") + matches := matchPaths(fixture, filter) + expected := []string{ + "{opt.node}.Children[0].Value.Value", + "{opt.node}.Children[0].Value.Value", + "{opt.node}.Children[1].Value.Value", + "{opt.node}.Children[1].Value.Value", + "{opt.node}.Children[2].Ref.Value.Value", + "{opt.node}.Children[2].Ref.Value.Value", + "{opt.node}.Children[2].Value.Value", + "{opt.node}.Children[2].Value.Value", + "{opt.node}.Value.Value", + } + assert.DeepEqual(t, matches, expected) +} + +func TestPathDebug(t *testing.T) { + fixture := node{ + Value: nodeValue{Value: 3}, + Children: []node{ + {Ref: &node{Value: nodeValue{Value: 9}}}, + }, + Labels: map[string]node{ + "label1": {}, + }, + } + gocmp.Equal(fixture, fixture, gocmp.FilterPath(PathDebug, gocmp.Ignore())) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/env/env.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/env/env.go new file mode 100644 index 0000000..a06eab3 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/env/env.go @@ -0,0 +1,120 @@ +/*Package env provides functions to test code that read environment variables +or the current working directory. +*/ +package env // import "gotest.tools/v3/env" + +import ( + "os" + "strings" + + "gotest.tools/v3/assert" + "gotest.tools/v3/internal/cleanup" +) + +type helperT interface { + Helper() +} + +// Patch changes the value of an environment variable, and returns a +// function which will reset the the value of that variable back to the +// previous state. +// +// When used with Go 1.14+ the unpatch function will be called automatically +// when the test ends, unless the TEST_NOCLEANUP env var is set to true. +// +// Deprecated: use t.SetEnv +func Patch(t assert.TestingT, key, value string) func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + oldValue, envVarExists := os.LookupEnv(key) + assert.NilError(t, os.Setenv(key, value)) + clean := func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !envVarExists { + assert.NilError(t, os.Unsetenv(key)) + return + } + assert.NilError(t, os.Setenv(key, oldValue)) + } + cleanup.Cleanup(t, clean) + return clean +} + +// PatchAll sets the environment to env, and returns a function which will +// reset the environment back to the previous state. +// +// When used with Go 1.14+ the unpatch function will be called automatically +// when the test ends, unless the TEST_NOCLEANUP env var is set to true. +func PatchAll(t assert.TestingT, env map[string]string) func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + oldEnv := os.Environ() + os.Clearenv() + + for key, value := range env { + assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value) + } + clean := func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + os.Clearenv() + for key, oldVal := range ToMap(oldEnv) { + assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal) + } + } + cleanup.Cleanup(t, clean) + return clean +} + +// ToMap takes a list of strings in the format returned by os.Environ() and +// returns a mapping of keys to values. +func ToMap(env []string) map[string]string { + result := map[string]string{} + for _, raw := range env { + key, value := getParts(raw) + result[key] = value + } + return result +} + +func getParts(raw string) (string, string) { + if raw == "" { + return "", "" + } + // Environment variables on windows can begin with = + // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx + parts := strings.SplitN(raw[1:], "=", 2) + key := raw[:1] + parts[0] + if len(parts) == 1 { + return key, "" + } + return key, parts[1] +} + +// ChangeWorkingDir to the directory, and return a function which restores the +// previous working directory. +// +// When used with Go 1.14+ the previous working directory will be restored +// automatically when the test ends, unless the TEST_NOCLEANUP env var is set to +// true. +func ChangeWorkingDir(t assert.TestingT, dir string) func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + cwd, err := os.Getwd() + assert.NilError(t, err) + assert.NilError(t, os.Chdir(dir)) + clean := func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert.NilError(t, os.Chdir(cwd)) + } + cleanup.Cleanup(t, clean) + return clean +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/env/env_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/env/env_test.go new file mode 100644 index 0000000..54458f2 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/env/env_test.go @@ -0,0 +1,170 @@ +package env + +import ( + "os" + "runtime" + "sort" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" + "gotest.tools/v3/internal/source" + "gotest.tools/v3/skip" +) + +func TestPatchFromUnset(t *testing.T) { + key, value := "FOO_IS_UNSET", "VALUE" + revert := Patch(t, key, value) + + assert.Assert(t, value == os.Getenv(key)) + revert() + _, isSet := os.LookupEnv(key) + assert.Assert(t, !isSet) +} + +func TestPatch(t *testing.T) { + skip.If(t, os.Getenv("PATH") == "") + oldVal := os.Getenv("PATH") + + key, value := "PATH", "NEWVALUE" + revert := Patch(t, key, value) + + assert.Assert(t, value == os.Getenv(key)) + revert() + assert.Assert(t, oldVal == os.Getenv(key)) +} + +func TestPatch_IntegrationWithCleanup(t *testing.T) { + skip.If(t, source.GoVersionLessThan(1, 14)) + + key := "totally_unique_env_var_key" + t.Run("cleanup in subtest", func(t *testing.T) { + Patch(t, key, "the-new-value") + assert.Equal(t, os.Getenv(key), "the-new-value") + }) + + t.Run("env var is unset", func(t *testing.T) { + v, ok := os.LookupEnv(key) + assert.Assert(t, !ok, "expected env var to be unset, got %v", v) + }) +} + +func TestPatchAll(t *testing.T) { + oldEnv := os.Environ() + newEnv := map[string]string{ + "FIRST": "STARS", + "THEN": "MOON", + } + + revert := PatchAll(t, newEnv) + + actual := os.Environ() + sort.Strings(actual) + assert.DeepEqual(t, []string{"FIRST=STARS", "THEN=MOON"}, actual) + + revert() + assert.DeepEqual(t, sorted(oldEnv), sorted(os.Environ())) +} + +func TestPatchAllWindows(t *testing.T) { + skip.If(t, runtime.GOOS != "windows") + oldEnv := os.Environ() + newEnv := map[string]string{ + "FIRST": "STARS", + "THEN": "MOON", + "=FINAL": "SUN", + "=BAR": "", + } + + revert := PatchAll(t, newEnv) + + actual := os.Environ() + sort.Strings(actual) + assert.DeepEqual(t, []string{"=BAR=", "=FINAL=SUN", "FIRST=STARS", "THEN=MOON"}, actual) + + revert() + assert.DeepEqual(t, sorted(oldEnv), sorted(os.Environ())) +} + +func sorted(source []string) []string { + sort.Strings(source) + return source +} + +func TestPatchAll_IntegrationWithCleanup(t *testing.T) { + skip.If(t, source.GoVersionLessThan(1, 14)) + + key := "totally_unique_env_var_key" + t.Run("cleanup in subtest", func(t *testing.T) { + PatchAll(t, map[string]string{key: "the-new-value"}) + assert.Equal(t, os.Getenv(key), "the-new-value") + }) + + t.Run("env var is unset", func(t *testing.T) { + v, ok := os.LookupEnv(key) + assert.Assert(t, !ok, "expected env var to be unset, got %v", v) + }) +} + +func TestToMap(t *testing.T) { + source := []string{ + "key=value", + "novaluekey", + "=foo=bar", + "z=singlecharkey", + "b", + "", + } + actual := ToMap(source) + expected := map[string]string{ + "key": "value", + "novaluekey": "", + "=foo": "bar", + "z": "singlecharkey", + "b": "", + "": "", + } + assert.DeepEqual(t, expected, actual) +} + +func TestChangeWorkingDir(t *testing.T) { + tmpDir := fs.NewDir(t, t.Name()) + defer tmpDir.Remove() + + origWorkDir := pwd(t) + + reset := ChangeWorkingDir(t, tmpDir.Path()) + t.Run("changed to dir", func(t *testing.T) { + assert.Equal(t, pwd(t), tmpDir.Path()) + }) + + t.Run("reset dir", func(t *testing.T) { + reset() + assert.Equal(t, pwd(t), origWorkDir) + }) +} + +func TestChangeWorkingDir_IntegrationWithCleanup(t *testing.T) { + skip.If(t, source.GoVersionLessThan(1, 14)) + + tmpDir := fs.NewDir(t, t.Name()) + defer tmpDir.Remove() + + origWorkDir := pwd(t) + + t.Run("cleanup in subtest", func(t *testing.T) { + ChangeWorkingDir(t, tmpDir.Path()) + assert.Equal(t, pwd(t), tmpDir.Path()) + }) + + t.Run("working dir is reset", func(t *testing.T) { + assert.Equal(t, pwd(t), origWorkDir) + }) +} + +func pwd(t *testing.T) string { + t.Helper() + dir, err := os.Getwd() + assert.NilError(t, err) + return dir +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/env/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/env/example_test.go new file mode 100644 index 0000000..b2d2f41 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/env/example_test.go @@ -0,0 +1,18 @@ +package env + +import "testing" + +var t = &testing.T{} + +// Patch an environment variable and defer to return to the previous state +func ExamplePatch() { + defer Patch(t, "PATH", "/custom/path")() +} + +// Patch all environment variables +func ExamplePatchAll() { + defer PatchAll(t, map[string]string{ + "ONE": "FOO", + "TWO": "BAR", + })() +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/example_test.go new file mode 100644 index 0000000..09ea38d --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/example_test.go @@ -0,0 +1,70 @@ +package fs_test + +import ( + "io/ioutil" + "os" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" + "gotest.tools/v3/golden" +) + +var t = &testing.T{} + +// Create a temporary directory which contains a single file +func ExampleNewDir() { + dir := fs.NewDir(t, "test-name", fs.WithFile("file1", "content\n")) + defer dir.Remove() + + files, err := ioutil.ReadDir(dir.Path()) + assert.NilError(t, err) + assert.Assert(t, cmp.Len(files, 0)) +} + +// Create a new file with some content +func ExampleNewFile() { + file := fs.NewFile(t, "test-name", fs.WithContent("content\n"), fs.AsUser(0, 0)) + defer file.Remove() + + content, err := ioutil.ReadFile(file.Path()) + assert.NilError(t, err) + assert.Equal(t, "content\n", content) +} + +// Create a directory and subdirectory with files +func ExampleWithDir() { + dir := fs.NewDir(t, "test-name", + fs.WithDir("subdir", + fs.WithMode(os.FileMode(0700)), + fs.WithFile("file1", "content\n")), + ) + defer dir.Remove() +} + +// Test that a directory contains the expected files, and all the files have the +// expected properties. +func ExampleEqual() { + path := operationWhichCreatesFiles() + expected := fs.Expected(t, + fs.WithFile("one", "", + fs.WithBytes(golden.Get(t, "one.golden")), + fs.WithMode(0600)), + fs.WithDir("data", + fs.WithFile("config", "", fs.MatchAnyFileContent))) + + assert.Assert(t, fs.Equal(path, expected)) +} + +func operationWhichCreatesFiles() string { + return "example-path" +} + +// Add a file to an existing directory +func ExampleApply() { + dir := fs.NewDir(t, "test-name") + defer dir.Remove() + + fs.Apply(t, dir, fs.WithFile("file1", "content\n")) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file.go new file mode 100644 index 0000000..3ca5660 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file.go @@ -0,0 +1,131 @@ +/*Package fs provides tools for creating temporary files, and testing the +contents and structure of a directory. +*/ +package fs // import "gotest.tools/v3/fs" + +import ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + + "gotest.tools/v3/assert" + "gotest.tools/v3/internal/cleanup" +) + +// Path objects return their filesystem path. Path may be implemented by a +// real filesystem object (such as File and Dir) or by a type which updates +// entries in a Manifest. +type Path interface { + Path() string + Remove() +} + +var ( + _ Path = &Dir{} + _ Path = &File{} +) + +// File is a temporary file on the filesystem +type File struct { + path string +} + +type helperT interface { + Helper() +} + +// NewFile creates a new file in a temporary directory using prefix as part of +// the filename. The PathOps are applied to the before returning the File. +// +// When used with Go 1.14+ the file will be automatically removed when the test +// ends, unless the TEST_NOCLEANUP env var is set to true. +func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + tempfile, err := ioutil.TempFile("", cleanPrefix(prefix)+"-") + assert.NilError(t, err) + + file := &File{path: tempfile.Name()} + cleanup.Cleanup(t, file.Remove) + + assert.NilError(t, tempfile.Close()) + assert.NilError(t, applyPathOps(file, ops)) + return file +} + +func cleanPrefix(prefix string) string { + // windows requires both / and \ are replaced + if runtime.GOOS == "windows" { + prefix = strings.Replace(prefix, string(os.PathSeparator), "-", -1) + } + return strings.Replace(prefix, "/", "-", -1) +} + +// Path returns the full path to the file +func (f *File) Path() string { + return f.path +} + +// Remove the file +func (f *File) Remove() { + // nolint: errcheck + os.Remove(f.path) +} + +// Dir is a temporary directory +type Dir struct { + path string +} + +// NewDir returns a new temporary directory using prefix as part of the directory +// name. The PathOps are applied before returning the Dir. +// +// When used with Go 1.14+ the directory will be automatically removed when the test +// ends, unless the TEST_NOCLEANUP env var is set to true. +func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + path, err := ioutil.TempDir("", cleanPrefix(prefix)+"-") + assert.NilError(t, err) + dir := &Dir{path: path} + cleanup.Cleanup(t, dir.Remove) + + assert.NilError(t, applyPathOps(dir, ops)) + return dir +} + +// Path returns the full path to the directory +func (d *Dir) Path() string { + return d.path +} + +// Remove the directory +func (d *Dir) Remove() { + // nolint: errcheck + os.RemoveAll(d.path) +} + +// Join returns a new path with this directory as the base of the path +func (d *Dir) Join(parts ...string) string { + return filepath.Join(append([]string{d.Path()}, parts...)...) +} + +// DirFromPath returns a Dir for a path that already exists. No directory is created. +// Unlike NewDir the directory will not be removed automatically when the test exits, +// it is the callers responsibly to remove the directory. +// DirFromPath can be used with Apply to modify an existing directory. +// +// If the path does not already exist, use NewDir instead. +func DirFromPath(t assert.TestingT, path string, ops ...PathOp) *Dir { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + dir := &Dir{path: path} + assert.NilError(t, applyPathOps(dir, ops)) + return dir +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file_test.go new file mode 100644 index 0000000..5e8be4d --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/file_test.go @@ -0,0 +1,118 @@ +package fs_test + +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" + "gotest.tools/v3/internal/source" + "gotest.tools/v3/skip" +) + +func TestNewDirWithOpsAndManifestEqual(t *testing.T) { + var userOps []fs.PathOp + if os.Geteuid() == 0 { + userOps = append(userOps, fs.AsUser(1001, 1002)) + } + + ops := []fs.PathOp{ + fs.WithFile("file1", "contenta", fs.WithMode(0400)), + fs.WithFile("file2", "", fs.WithBytes([]byte{0, 1, 2})), + fs.WithFile("file5", "", userOps...), + fs.WithSymlink("link1", "file1"), + fs.WithDir("sub", + fs.WithFiles(map[string]string{ + "file3": "contentb", + "file4": "contentc", + }), + fs.WithMode(0705), + ), + } + + dir := fs.NewDir(t, "test-all", ops...) + defer dir.Remove() + + manifestOps := append( + ops[:3], + fs.WithSymlink("link1", dir.Join("file1")), + ops[4], + ) + assert.Assert(t, fs.Equal(dir.Path(), fs.Expected(t, manifestOps...))) +} + +func TestNewFile(t *testing.T) { + t.Run("with test name", func(t *testing.T) { + tmpFile := fs.NewFile(t, t.Name()) + _, err := os.Stat(tmpFile.Path()) + assert.NilError(t, err) + + tmpFile.Remove() + _, err = os.Stat(tmpFile.Path()) + assert.ErrorType(t, err, os.IsNotExist) + }) + + t.Run(`with \ in name`, func(t *testing.T) { + tmpFile := fs.NewFile(t, `foo\thing`) + _, err := os.Stat(tmpFile.Path()) + assert.NilError(t, err) + + tmpFile.Remove() + _, err = os.Stat(tmpFile.Path()) + assert.ErrorType(t, err, os.IsNotExist) + }) +} + +func TestNewFile_IntegrationWithCleanup(t *testing.T) { + skip.If(t, source.GoVersionLessThan(1, 14)) + var tmpFile *fs.File + t.Run("cleanup in subtest", func(t *testing.T) { + tmpFile = fs.NewFile(t, t.Name()) + _, err := os.Stat(tmpFile.Path()) + assert.NilError(t, err) + }) + + t.Run("file has been removed", func(t *testing.T) { + _, err := os.Stat(tmpFile.Path()) + assert.ErrorType(t, err, os.IsNotExist) + }) +} + +func TestNewDir_IntegrationWithCleanup(t *testing.T) { + skip.If(t, source.GoVersionLessThan(1, 14)) + var tmpFile *fs.Dir + t.Run("cleanup in subtest", func(t *testing.T) { + tmpFile = fs.NewDir(t, t.Name()) + _, err := os.Stat(tmpFile.Path()) + assert.NilError(t, err) + }) + + t.Run("dir has been removed", func(t *testing.T) { + _, err := os.Stat(tmpFile.Path()) + assert.ErrorType(t, err, os.IsNotExist) + }) +} + +func TestDirFromPath(t *testing.T) { + tmpdir, err := ioutil.TempDir("", t.Name()) + assert.NilError(t, err) + t.Cleanup(func() { + os.RemoveAll(tmpdir) + }) + + dir := fs.DirFromPath(t, tmpdir, fs.WithFile("newfile", "")) + + _, err = os.Stat(dir.Join("newfile")) + assert.NilError(t, err) + + assert.Equal(t, dir.Path(), tmpdir) + assert.Equal(t, dir.Join("newfile"), filepath.Join(tmpdir, "newfile")) + + dir.Remove() + + _, err = os.Stat(tmpdir) + assert.Assert(t, errors.Is(err, os.ErrNotExist)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest.go new file mode 100644 index 0000000..b657bd9 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest.go @@ -0,0 +1,139 @@ +package fs + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "gotest.tools/v3/assert" +) + +// Manifest stores the expected structure and properties of files and directories +// in a filesystem. +type Manifest struct { + root *directory +} + +type resource struct { + mode os.FileMode + uid uint32 + gid uint32 +} + +type file struct { + resource + content io.ReadCloser + ignoreCariageReturn bool + compareContentFunc func(b []byte) CompareResult +} + +func (f *file) Type() string { + return "file" +} + +type symlink struct { + resource + target string +} + +func (f *symlink) Type() string { + return "symlink" +} + +type directory struct { + resource + items map[string]dirEntry + filepathGlobs map[string]*filePath +} + +func (f *directory) Type() string { + return "directory" +} + +type dirEntry interface { + Type() string +} + +// ManifestFromDir creates a Manifest by reading the directory at path. The +// manifest stores the structure and properties of files in the directory. +// ManifestFromDir can be used with Equal to compare two directories. +func ManifestFromDir(t assert.TestingT, path string) Manifest { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + manifest, err := manifestFromDir(path) + assert.NilError(t, err) + return manifest +} + +func manifestFromDir(path string) (Manifest, error) { + info, err := os.Stat(path) + switch { + case err != nil: + return Manifest{}, err + case !info.IsDir(): + return Manifest{}, fmt.Errorf("path %s must be a directory", path) + } + + directory, err := newDirectory(path, info) + return Manifest{root: directory}, err +} + +func newDirectory(path string, info os.FileInfo) (*directory, error) { + items := make(map[string]dirEntry) + children, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + for _, child := range children { + fullPath := filepath.Join(path, child.Name()) + items[child.Name()], err = getTypedResource(fullPath, child) + if err != nil { + return nil, err + } + } + + return &directory{ + resource: newResourceFromInfo(info), + items: items, + filepathGlobs: make(map[string]*filePath), + }, nil +} + +func getTypedResource(path string, info os.FileInfo) (dirEntry, error) { + switch { + case info.IsDir(): + return newDirectory(path, info) + case info.Mode()&os.ModeSymlink != 0: + return newSymlink(path, info) + // TODO: devices, pipes? + default: + return newFile(path, info) + } +} + +func newSymlink(path string, info os.FileInfo) (*symlink, error) { + target, err := os.Readlink(path) + if err != nil { + return nil, err + } + return &symlink{ + resource: newResourceFromInfo(info), + target: target, + }, err +} + +func newFile(path string, info os.FileInfo) (*file, error) { + // TODO: defer file opening to reduce number of open FDs? + readCloser, err := os.Open(path) + if err != nil { + return nil, err + } + return &file{ + resource: newResourceFromInfo(info), + content: readCloser, + }, err +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_test.go new file mode 100644 index 0000000..2cbc610 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_test.go @@ -0,0 +1,110 @@ +package fs + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "runtime" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "gotest.tools/v3/assert" +) + +func TestManifestFromDir(t *testing.T) { + var defaultFileMode os.FileMode = 0644 + var subDirMode = 0755 | os.ModeDir + var jFileMode os.FileMode = 0600 + if runtime.GOOS == "windows" { + defaultFileMode = 0666 + subDirMode = 0777 | os.ModeDir + jFileMode = 0666 + } + + var userOps []PathOp + var expectedUserResource = newResource(defaultFileMode) + if os.Geteuid() == 0 { + userOps = append(userOps, AsUser(1001, 1002)) + expectedUserResource = resource{mode: defaultFileMode, uid: 1001, gid: 1002} + } + + srcDir := NewDir(t, t.Name(), + WithFile("j", "content j", WithMode(0600)), + WithDir("s", + WithFile("k", "content k")), + WithSymlink("f", "j"), + WithFile("x", "content x", userOps...)) + defer srcDir.Remove() + + expected := Manifest{ + root: &directory{ + resource: newResource(defaultRootDirMode), + items: map[string]dirEntry{ + "j": &file{ + resource: newResource(jFileMode), + content: readCloser("content j"), + }, + "s": &directory{ + resource: newResource(subDirMode), + items: map[string]dirEntry{ + "k": &file{ + resource: newResource(defaultFileMode), + content: readCloser("content k"), + }, + }, + filepathGlobs: map[string]*filePath{}, + }, + "f": &symlink{ + resource: newResource(defaultSymlinkMode), + target: srcDir.Join("j"), + }, + "x": &file{ + resource: expectedUserResource, + content: readCloser("content x"), + }, + }, + filepathGlobs: map[string]*filePath{}, + }, + } + actual := ManifestFromDir(t, srcDir.Path()) + assert.DeepEqual(t, actual, expected, cmpManifest) + actual.root.items["j"].(*file).content.Close() + actual.root.items["x"].(*file).content.Close() + actual.root.items["s"].(*directory).items["k"].(*file).content.Close() +} + +func TestSymlinks(t *testing.T) { + rootDirectory := NewDir(t, "root", + WithFile("foo.txt", "foo"), + WithSymlink("foo.link", "foo.txt")) + defer rootDirectory.Remove() + expected := Expected(t, + WithFile("foo.txt", "foo"), + WithSymlink("foo.link", rootDirectory.Join("foo.txt"))) + assert.Assert(t, Equal(rootDirectory.Path(), expected)) +} + +var cmpManifest = cmp.Options{ + cmp.AllowUnexported(Manifest{}, resource{}, file{}, symlink{}, directory{}), + cmp.Comparer(func(x, y io.ReadCloser) bool { + if x == nil || y == nil { + return x == y + } + xContent, err := ioutil.ReadAll(x) + if err != nil { + return false + } + + yContent, err := ioutil.ReadAll(y) + if err != nil { + return false + } + return bytes.Equal(xContent, yContent) + }), +} + +func readCloser(s string) io.ReadCloser { + return ioutil.NopCloser(strings.NewReader(s)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_unix.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_unix.go new file mode 100644 index 0000000..d2956f3 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_unix.go @@ -0,0 +1,38 @@ +//go:build !windows +// +build !windows + +package fs + +import ( + "os" + "runtime" + "syscall" +) + +const defaultRootDirMode = os.ModeDir | 0700 + +var defaultSymlinkMode = os.ModeSymlink | 0777 + +func init() { + switch runtime.GOOS { + case "darwin": + defaultSymlinkMode = os.ModeSymlink | 0755 + } +} + +func newResourceFromInfo(info os.FileInfo) resource { + statT := info.Sys().(*syscall.Stat_t) + return resource{ + mode: info.Mode(), + uid: statT.Uid, + gid: statT.Gid, + } +} + +func (p *filePath) SetMode(mode os.FileMode) { + p.file.mode = mode +} + +func (p *directoryPath) SetMode(mode os.FileMode) { + p.directory.mode = mode | os.ModeDir +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_windows.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_windows.go new file mode 100644 index 0000000..1c1a093 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/manifest_windows.go @@ -0,0 +1,22 @@ +package fs + +import "os" + +const ( + defaultRootDirMode = os.ModeDir | 0777 + defaultSymlinkMode = os.ModeSymlink | 0666 +) + +func newResourceFromInfo(info os.FileInfo) resource { + return resource{mode: info.Mode()} +} + +func (p *filePath) SetMode(mode os.FileMode) { + bits := mode & 0600 + p.file.mode = bits + bits/010 + bits/0100 +} + +// TODO: is mode ignored on windows? +func (p *directoryPath) SetMode(mode os.FileMode) { + p.directory.mode = defaultRootDirMode +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops.go new file mode 100644 index 0000000..9e1e068 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops.go @@ -0,0 +1,275 @@ +package fs + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "gotest.tools/v3/assert" +) + +const defaultFileMode = 0644 + +// PathOp is a function which accepts a Path and performs an operation on that +// path. When called with real filesystem objects (File or Dir) a PathOp modifies +// the filesystem at the path. When used with a Manifest object a PathOp updates +// the manifest to expect a value. +type PathOp func(path Path) error + +type manifestResource interface { + SetMode(mode os.FileMode) + SetUID(uid uint32) + SetGID(gid uint32) +} + +type manifestFile interface { + manifestResource + SetContent(content io.ReadCloser) +} + +type manifestDirectory interface { + manifestResource + AddSymlink(path, target string) error + AddFile(path string, ops ...PathOp) error + AddDirectory(path string, ops ...PathOp) error +} + +// WithContent writes content to a file at Path +func WithContent(content string) PathOp { + return func(path Path) error { + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(strings.NewReader(content))) + return nil + } + return ioutil.WriteFile(path.Path(), []byte(content), defaultFileMode) + } +} + +// WithBytes write bytes to a file at Path +func WithBytes(raw []byte) PathOp { + return func(path Path) error { + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(bytes.NewReader(raw))) + return nil + } + return ioutil.WriteFile(path.Path(), raw, defaultFileMode) + } +} + +// WithReaderContent copies the reader contents to the file at Path +func WithReaderContent(r io.Reader) PathOp { + return func(path Path) error { + if m, ok := path.(manifestFile); ok { + m.SetContent(ioutil.NopCloser(r)) + return nil + } + f, err := os.OpenFile(path.Path(), os.O_WRONLY, defaultFileMode) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, r) + return err + } +} + +// AsUser changes ownership of the file system object at Path +func AsUser(uid, gid int) PathOp { + return func(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetUID(uint32(uid)) + m.SetGID(uint32(gid)) + return nil + } + return os.Chown(path.Path(), uid, gid) + } +} + +// WithFile creates a file in the directory at path with content +func WithFile(filename, content string, ops ...PathOp) PathOp { + return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + ops = append([]PathOp{WithContent(content), WithMode(defaultFileMode)}, ops...) + return m.AddFile(filename, ops...) + } + + fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename)) + if err := createFile(fullpath, content); err != nil { + return err + } + return applyPathOps(&File{path: fullpath}, ops) + } +} + +func createFile(fullpath string, content string) error { + return ioutil.WriteFile(fullpath, []byte(content), defaultFileMode) +} + +// WithFiles creates all the files in the directory at path with their content +func WithFiles(files map[string]string) PathOp { + return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + for filename, content := range files { + // TODO: remove duplication with WithFile + if err := m.AddFile(filename, WithContent(content), WithMode(defaultFileMode)); err != nil { + return err + } + } + return nil + } + + for filename, content := range files { + fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename)) + if err := createFile(fullpath, content); err != nil { + return err + } + } + return nil + } +} + +// FromDir copies the directory tree from the source path into the new Dir +func FromDir(source string) PathOp { + return func(path Path) error { + if _, ok := path.(manifestDirectory); ok { + return fmt.Errorf("use manifest.FromDir") + } + return copyDirectory(source, path.Path()) + } +} + +// WithDir creates a subdirectory in the directory at path. Additional PathOp +// can be used to modify the subdirectory +func WithDir(name string, ops ...PathOp) PathOp { + const defaultMode = 0755 + return func(path Path) error { + if m, ok := path.(manifestDirectory); ok { + ops = append([]PathOp{WithMode(defaultMode)}, ops...) + return m.AddDirectory(name, ops...) + } + + fullpath := filepath.Join(path.Path(), filepath.FromSlash(name)) + err := os.MkdirAll(fullpath, defaultMode) + if err != nil { + return err + } + return applyPathOps(&Dir{path: fullpath}, ops) + } +} + +// Apply the PathOps to the File +func Apply(t assert.TestingT, path Path, ops ...PathOp) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert.NilError(t, applyPathOps(path, ops)) +} + +func applyPathOps(path Path, ops []PathOp) error { + for _, op := range ops { + if err := op(path); err != nil { + return err + } + } + return nil +} + +// WithMode sets the file mode on the directory or file at path +func WithMode(mode os.FileMode) PathOp { + return func(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetMode(mode) + return nil + } + return os.Chmod(path.Path(), mode) + } +} + +func copyDirectory(source, dest string) error { + entries, err := ioutil.ReadDir(source) + if err != nil { + return err + } + for _, entry := range entries { + sourcePath := filepath.Join(source, entry.Name()) + destPath := filepath.Join(dest, entry.Name()) + switch { + case entry.IsDir(): + if err := os.Mkdir(destPath, 0755); err != nil { + return err + } + if err := copyDirectory(sourcePath, destPath); err != nil { + return err + } + case entry.Mode()&os.ModeSymlink != 0: + if err := copySymLink(sourcePath, destPath); err != nil { + return err + } + default: + if err := copyFile(sourcePath, destPath); err != nil { + return err + } + } + } + return nil +} + +func copySymLink(source, dest string) error { + link, err := os.Readlink(source) + if err != nil { + return err + } + return os.Symlink(link, dest) +} + +func copyFile(source, dest string) error { + content, err := ioutil.ReadFile(source) + if err != nil { + return err + } + return ioutil.WriteFile(dest, content, 0644) +} + +// WithSymlink creates a symlink in the directory which links to target. +// Target must be a path relative to the directory. +// +// Note: the argument order is the inverse of os.Symlink to be consistent with +// the other functions in this package. +func WithSymlink(path, target string) PathOp { + return func(root Path) error { + if v, ok := root.(manifestDirectory); ok { + return v.AddSymlink(path, target) + } + return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) + } +} + +// WithHardlink creates a link in the directory which links to target. +// Target must be a path relative to the directory. +// +// Note: the argument order is the inverse of os.Link to be consistent with +// the other functions in this package. +func WithHardlink(path, target string) PathOp { + return func(root Path) error { + if _, ok := root.(manifestDirectory); ok { + return fmt.Errorf("WithHardlink not implemented for manifests") + } + return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) + } +} + +// WithTimestamps sets the access and modification times of the file system object +// at path. +func WithTimestamps(atime, mtime time.Time) PathOp { + return func(root Path) error { + if _, ok := root.(manifestDirectory); ok { + return fmt.Errorf("WithTimestamp not implemented for manifests") + } + return os.Chtimes(root.Path(), atime, mtime) + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops_test.go new file mode 100644 index 0000000..7185f2a --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/ops_test.go @@ -0,0 +1,100 @@ +package fs_test + +import ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + "testing" + "time" + + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" +) + +func TestFromDir(t *testing.T) { + dir := fs.NewDir(t, "test-from-dir", fs.FromDir("testdata/copy-test")) + defer dir.Remove() + + expected := fs.Expected(t, + fs.WithFile("1", "1\n"), + fs.WithDir("a", + fs.WithFile("1", "1\n"), + fs.WithFile("2", "2\n"), + fs.WithDir("b", + fs.WithFile("1", "1\n")))) + + assert.Assert(t, fs.Equal(dir.Path(), expected)) +} + +func TestFromDirSymlink(t *testing.T) { + dir := fs.NewDir(t, "test-from-dir", fs.FromDir("testdata/copy-test-with-symlink")) + defer dir.Remove() + + currentdir, err := os.Getwd() + assert.NilError(t, err) + + link2 := filepath.FromSlash("../2") + link3 := "/some/inexistent/link" + if runtime.GOOS == "windows" { + link3 = filepath.Join(filepath.VolumeName(currentdir), link3) + } + + expected := fs.Expected(t, + fs.WithFile("1", "1\n"), + fs.WithDir("a", + fs.WithFile("1", "1\n"), + fs.WithFile("2", "2\n"), + fs.WithDir("b", + fs.WithFile("1", "1\n"), + fs.WithSymlink("2", link2), + fs.WithSymlink("3", link3), + fs.WithSymlink("4", "5"), + ))) + + assert.Assert(t, fs.Equal(dir.Path(), expected)) +} + +func TestWithTimestamps(t *testing.T) { + stamp := time.Date(2011, 11, 11, 5, 55, 55, 0, time.UTC) + tmpFile := fs.NewFile(t, t.Name(), fs.WithTimestamps(stamp, stamp)) + defer tmpFile.Remove() + + stat, err := os.Stat(tmpFile.Path()) + assert.NilError(t, err) + assert.DeepEqual(t, stat.ModTime(), stamp) +} + +func TestApply(t *testing.T) { + t.Run("with file", func(t *testing.T) { + tmpFile := fs.NewFile(t, "test-update-file", fs.WithContent("contenta")) + defer tmpFile.Remove() + fs.Apply(t, tmpFile, fs.WithContent("contentb")) + content, err := ioutil.ReadFile(tmpFile.Path()) + assert.NilError(t, err) + assert.Equal(t, string(content), "contentb") + }) + + t.Run("with dir", func(t *testing.T) { + tmpDir := fs.NewDir(t, "test-update-dir") + defer tmpDir.Remove() + fs.Apply(t, tmpDir, fs.WithFile("file1", "contenta")) + fs.Apply(t, tmpDir, fs.WithFile("file2", "contentb")) + expected := fs.Expected(t, + fs.WithFile("file1", "contenta"), + fs.WithFile("file2", "contentb")) + assert.Assert(t, fs.Equal(tmpDir.Path(), expected)) + }) +} + +func TestWithReaderContent(t *testing.T) { + content := "this is a test" + dir := fs.NewDir(t, t.Name(), + fs.WithFile("1", "", + fs.WithReaderContent(strings.NewReader(content))), + ) + defer dir.Remove() + expected := fs.Expected(t, fs.WithFile("1", content)) + assert.Assert(t, fs.Equal(dir.Path(), expected)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/path.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/path.go new file mode 100644 index 0000000..c301b90 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/path.go @@ -0,0 +1,199 @@ +package fs + +import ( + "bytes" + "io" + "io/ioutil" + "os" + + "gotest.tools/v3/assert" +) + +// resourcePath is an adaptor for resources so they can be used as a Path +// with PathOps. +type resourcePath struct{} + +func (p *resourcePath) Path() string { + return "manifest: not a filesystem path" +} + +func (p *resourcePath) Remove() {} + +type filePath struct { + resourcePath + file *file +} + +func (p *filePath) SetContent(content io.ReadCloser) { + p.file.content = content +} + +func (p *filePath) SetUID(uid uint32) { + p.file.uid = uid +} + +func (p *filePath) SetGID(gid uint32) { + p.file.gid = gid +} + +type directoryPath struct { + resourcePath + directory *directory +} + +func (p *directoryPath) SetUID(uid uint32) { + p.directory.uid = uid +} + +func (p *directoryPath) SetGID(gid uint32) { + p.directory.gid = gid +} + +func (p *directoryPath) AddSymlink(path, target string) error { + p.directory.items[path] = &symlink{ + resource: newResource(defaultSymlinkMode), + target: target, + } + return nil +} + +func (p *directoryPath) AddFile(path string, ops ...PathOp) error { + newFile := &file{resource: newResource(0)} + p.directory.items[path] = newFile + exp := &filePath{file: newFile} + return applyPathOps(exp, ops) +} + +func (p *directoryPath) AddGlobFiles(glob string, ops ...PathOp) error { + newFile := &file{resource: newResource(0)} + newFilePath := &filePath{file: newFile} + p.directory.filepathGlobs[glob] = newFilePath + return applyPathOps(newFilePath, ops) +} + +func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error { + newDir := newDirectoryWithDefaults() + p.directory.items[path] = newDir + exp := &directoryPath{directory: newDir} + return applyPathOps(exp, ops) +} + +// Expected returns a Manifest with a directory structured created by ops. The +// PathOp operations are applied to the manifest as expectations of the +// filesystem structure and properties. +func Expected(t assert.TestingT, ops ...PathOp) Manifest { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + + newDir := newDirectoryWithDefaults() + e := &directoryPath{directory: newDir} + assert.NilError(t, applyPathOps(e, ops)) + return Manifest{root: newDir} +} + +func newDirectoryWithDefaults() *directory { + return &directory{ + resource: newResource(defaultRootDirMode), + items: make(map[string]dirEntry), + filepathGlobs: make(map[string]*filePath), + } +} + +func newResource(mode os.FileMode) resource { + return resource{ + mode: mode, + uid: currentUID(), + gid: currentGID(), + } +} + +func currentUID() uint32 { + return normalizeID(os.Getuid()) +} + +func currentGID() uint32 { + return normalizeID(os.Getgid()) +} + +func normalizeID(id int) uint32 { + // ids will be -1 on windows + if id < 0 { + return 0 + } + return uint32(id) +} + +var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil)) + +// MatchAnyFileContent is a PathOp that updates a Manifest so that the file +// at path may contain any content. +func MatchAnyFileContent(path Path) error { + if m, ok := path.(*filePath); ok { + m.SetContent(anyFileContent) + } + return nil +} + +// MatchContentIgnoreCarriageReturn is a PathOp that ignores cariage return +// discrepancies. +func MatchContentIgnoreCarriageReturn(path Path) error { + if m, ok := path.(*filePath); ok { + m.file.ignoreCariageReturn = true + } + return nil +} + +const anyFile = "*" + +// MatchExtraFiles is a PathOp that updates a Manifest to allow a directory +// to contain unspecified files. +func MatchExtraFiles(path Path) error { + if m, ok := path.(*directoryPath); ok { + return m.AddFile(anyFile) + } + return nil +} + +// CompareResult is the result of comparison. +// +// See gotest.tools/assert/cmp.StringResult for a convenient implementation of +// this interface. +type CompareResult interface { + Success() bool + FailureMessage() string +} + +// MatchFileContent is a PathOp that updates a Manifest to use the provided +// function to determine if a file's content matches the expectation. +func MatchFileContent(f func([]byte) CompareResult) PathOp { + return func(path Path) error { + if m, ok := path.(*filePath); ok { + m.file.compareContentFunc = f + } + return nil + } +} + +// MatchFilesWithGlob is a PathOp that updates a Manifest to match files using +// glob pattern, and check them using the ops. +func MatchFilesWithGlob(glob string, ops ...PathOp) PathOp { + return func(path Path) error { + if m, ok := path.(*directoryPath); ok { + return m.AddGlobFiles(glob, ops...) + } + return nil + } +} + +// anyFileMode is represented by uint32_max +const anyFileMode os.FileMode = 4294967295 + +// MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path +// will match any file mode. +func MatchAnyFileMode(path Path) error { + if m, ok := path.(manifestResource); ok { + m.SetMode(anyFileMode) + } + return nil +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report.go new file mode 100644 index 0000000..1a3c668 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report.go @@ -0,0 +1,276 @@ +package fs + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "sort" + "strings" + + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/format" +) + +// Equal compares a directory to the expected structured described by a manifest +// and returns success if they match. If they do not match the failure message +// will contain all the differences between the directory structure and the +// expected structure defined by the Manifest. +// +// Equal is a cmp.Comparison which can be used with assert.Assert(). +func Equal(path string, expected Manifest) cmp.Comparison { + return func() cmp.Result { + actual, err := manifestFromDir(path) + if err != nil { + return cmp.ResultFromError(err) + } + failures := eqDirectory(string(os.PathSeparator), expected.root, actual.root) + if len(failures) == 0 { + return cmp.ResultSuccess + } + msg := fmt.Sprintf("directory %s does not match expected:\n", path) + return cmp.ResultFailure(msg + formatFailures(failures)) + } +} + +type failure struct { + path string + problems []problem +} + +type problem string + +func notEqual(property string, x, y interface{}) problem { + return problem(fmt.Sprintf("%s: expected %s got %s", property, x, y)) +} + +func errProblem(reason string, err error) problem { + return problem(fmt.Sprintf("%s: %s", reason, err)) +} + +func existenceProblem(filename, reason string, args ...interface{}) problem { + return problem(filename + ": " + fmt.Sprintf(reason, args...)) +} + +func eqResource(x, y resource) []problem { + var p []problem + if x.uid != y.uid { + p = append(p, notEqual("uid", x.uid, y.uid)) + } + if x.gid != y.gid { + p = append(p, notEqual("gid", x.gid, y.gid)) + } + if x.mode != anyFileMode && x.mode != y.mode { + p = append(p, notEqual("mode", x.mode, y.mode)) + } + return p +} + +func removeCarriageReturn(in []byte) []byte { + return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) +} + +func eqFile(x, y *file) []problem { + p := eqResource(x.resource, y.resource) + + switch { + case x.content == nil: + p = append(p, existenceProblem("content", "expected content is nil")) + return p + case x.content == anyFileContent: + return p + case y.content == nil: + p = append(p, existenceProblem("content", "actual content is nil")) + return p + } + + xContent, xErr := ioutil.ReadAll(x.content) + defer x.content.Close() + yContent, yErr := ioutil.ReadAll(y.content) + defer y.content.Close() + + if xErr != nil { + p = append(p, errProblem("failed to read expected content", xErr)) + } + if yErr != nil { + p = append(p, errProblem("failed to read actual content", xErr)) + } + if xErr != nil || yErr != nil { + return p + } + + if x.compareContentFunc != nil { + r := x.compareContentFunc(yContent) + if !r.Success() { + p = append(p, existenceProblem("content", r.FailureMessage())) + } + return p + } + + if x.ignoreCariageReturn || y.ignoreCariageReturn { + xContent = removeCarriageReturn(xContent) + yContent = removeCarriageReturn(yContent) + } + + if !bytes.Equal(xContent, yContent) { + p = append(p, diffContent(xContent, yContent)) + } + return p +} + +func diffContent(x, y []byte) problem { + diff := format.UnifiedDiff(format.DiffConfig{ + A: string(x), + B: string(y), + From: "expected", + To: "actual", + }) + // Remove the trailing newline in the diff. A trailing newline is always + // added to a problem by formatFailures. + diff = strings.TrimSuffix(diff, "\n") + return problem("content:\n" + indent(diff, " ")) +} + +func indent(s, prefix string) string { + buf := new(bytes.Buffer) + lines := strings.SplitAfter(s, "\n") + for _, line := range lines { + buf.WriteString(prefix + line) + } + return buf.String() +} + +func eqSymlink(x, y *symlink) []problem { + p := eqResource(x.resource, y.resource) + xTarget := x.target + yTarget := y.target + if runtime.GOOS == "windows" { + xTarget = strings.ToLower(xTarget) + yTarget = strings.ToLower(yTarget) + } + if xTarget != yTarget { + p = append(p, notEqual("target", x.target, y.target)) + } + return p +} + +func eqDirectory(path string, x, y *directory) []failure { + p := eqResource(x.resource, y.resource) + var f []failure + matchedFiles := make(map[string]bool) + + for _, name := range sortedKeys(x.items) { + if name == anyFile { + continue + } + matchedFiles[name] = true + xEntry := x.items[name] + yEntry, ok := y.items[name] + if !ok { + p = append(p, existenceProblem(name, "expected %s to exist", xEntry.Type())) + continue + } + + if xEntry.Type() != yEntry.Type() { + p = append(p, notEqual(name, xEntry.Type(), yEntry.Type())) + continue + } + + f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...) + } + + if len(x.filepathGlobs) != 0 { + for _, name := range sortedKeys(y.items) { + m := matchGlob(name, y.items[name], x.filepathGlobs) + matchedFiles[name] = m.match + f = append(f, m.failures...) + } + } + + if _, ok := x.items[anyFile]; ok { + return maybeAppendFailure(f, path, p) + } + for _, name := range sortedKeys(y.items) { + if !matchedFiles[name] { + p = append(p, existenceProblem(name, "unexpected %s", y.items[name].Type())) + } + } + return maybeAppendFailure(f, path, p) +} + +func maybeAppendFailure(failures []failure, path string, problems []problem) []failure { + if len(problems) > 0 { + return append(failures, failure{path: path, problems: problems}) + } + return failures +} + +func sortedKeys(items map[string]dirEntry) []string { + keys := make([]string, 0, len(items)) + for key := range items { + keys = append(keys, key) + } + sort.Strings(keys) + return keys +} + +// eqEntry assumes x and y to be the same type +func eqEntry(path string, x, y dirEntry) []failure { + resp := func(problems []problem) []failure { + if len(problems) == 0 { + return nil + } + return []failure{{path: path, problems: problems}} + } + + switch typed := x.(type) { + case *file: + return resp(eqFile(typed, y.(*file))) + case *symlink: + return resp(eqSymlink(typed, y.(*symlink))) + case *directory: + return eqDirectory(path, typed, y.(*directory)) + } + return nil +} + +type globMatch struct { + match bool + failures []failure +} + +func matchGlob(name string, yEntry dirEntry, globs map[string]*filePath) globMatch { + m := globMatch{} + + for glob, expectedFile := range globs { + ok, err := filepath.Match(glob, name) + if err != nil { + p := errProblem("failed to match glob pattern", err) + f := failure{path: name, problems: []problem{p}} + m.failures = append(m.failures, f) + } + if ok { + m.match = true + m.failures = eqEntry(name, expectedFile.file, yEntry) + return m + } + } + return m +} + +func formatFailures(failures []failure) string { + sort.Slice(failures, func(i, j int) bool { + return failures[i].path < failures[j].path + }) + + buf := new(bytes.Buffer) + for _, failure := range failures { + buf.WriteString(failure.path + "\n") + for _, problem := range failure.problems { + buf.WriteString(" " + string(problem) + "\n") + } + } + return buf.String() +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report_test.go new file mode 100644 index 0000000..389d608 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/report_test.go @@ -0,0 +1,273 @@ +package fs + +import ( + "fmt" + "path/filepath" + "runtime" + "testing" + + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/skip" +) + +func TestEqualMissingRoot(t *testing.T) { + result := Equal("/bogus/path/does/not/exist", Expected(t))() + assert.Assert(t, !result.Success()) + expected := "stat /bogus/path/does/not/exist: no such file or directory" + if runtime.GOOS == "windows" { + expected = "CreateFile /bogus/path/does/not/exist" + } + assert.Assert(t, is.Contains(result.(cmpFailure).FailureMessage(), expected)) +} + +func TestEqualModeMismatch(t *testing.T) { + dir := NewDir(t, t.Name(), WithMode(0500)) + defer dir.Remove() + + result := Equal(dir.Path(), Expected(t))() + assert.Assert(t, !result.Success()) + expected := fmtExpected(`directory %s does not match expected: +/ + mode: expected drwx------ got dr-x------ +`, dir.Path()) + if runtime.GOOS == "windows" { + expected = fmtExpected(`directory %s does not match expected: +\ + mode: expected drwxrwxrwx got dr-xr-xr-x +`, dir.Path()) + } + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) +} + +func TestEqualRootIsAFile(t *testing.T) { + file := NewFile(t, t.Name()) + defer file.Remove() + + result := Equal(file.Path(), Expected(t))() + assert.Assert(t, !result.Success()) + expected := fmt.Sprintf("path %s must be a directory", file.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) +} + +func TestEqualSuccess(t *testing.T) { + dir := NewDir(t, t.Name(), WithMode(0700)) + defer dir.Remove() + + assert.Assert(t, Equal(dir.Path(), Expected(t))) +} + +func TestEqualDirectoryHasWithExtraFiles(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("extra1", "content")) + defer dir.Remove() + + manifest := Expected(t, WithFile("file1", "content")) + result := Equal(dir.Path(), manifest)() + assert.Assert(t, !result.Success()) + expected := fmtExpected(`directory %s does not match expected: +/ + file1: expected file to exist + extra1: unexpected file +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) +} + +func fmtExpected(format string, args ...interface{}) string { + return filepath.FromSlash(fmt.Sprintf(format, args...)) +} + +func TestEqualWithMatchAnyFileContent(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("data", "this is some data")) + defer dir.Remove() + + expected := Expected(t, + WithFile("data", "different content", MatchAnyFileContent)) + assert.Assert(t, Equal(dir.Path(), expected)) +} + +func TestEqualWithFileContent(t *testing.T) { + dir := NewDir(t, "assert-test-root", + WithFile("file1", "line1\nline2\nline3")) + defer dir.Remove() + + manifest := Expected(t, + WithFile("file1", "line2\nline3")) + + result := Equal(dir.Path(), manifest)() + expected := fmtExpected(`directory %s does not match expected: +/file1 + content: + --- expected + +++ actual + @@ -1,2 +1,3 @@ + +line1 + line2 + line3 +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) +} + +func TestEqualWithMatchContentIgnoreCarriageReturn(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("file1", "line1\r\nline2")) + defer dir.Remove() + + manifest := Expected(t, + WithFile("file1", "line1\nline2", MatchContentIgnoreCarriageReturn)) + + result := Equal(dir.Path(), manifest)() + assert.Assert(t, result.Success()) +} + +func TestEqualDirectoryWithMatchExtraFiles(t *testing.T) { + file1 := WithFile("file1", "same in both") + dir := NewDir(t, t.Name(), + file1, + WithFile("extra", "some content")) + defer dir.Remove() + + expected := Expected(t, file1, MatchExtraFiles) + assert.Assert(t, Equal(dir.Path(), expected)) +} + +func TestEqualManyFailures(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("file1", "same in both"), + WithFile("extra", "some content"), + WithSymlink("sym1", "extra")) + defer dir.Remove() + + manifest := Expected(t, + WithDir("subdir", + WithFile("somefile", "")), + WithFile("file1", "not the\nsame in both")) + + result := Equal(dir.Path(), manifest)() + assert.Assert(t, !result.Success()) + + expected := fmtExpected(`directory %s does not match expected: +/ + subdir: expected directory to exist + extra: unexpected file + sym1: unexpected symlink +/file1 + content: + --- expected + +++ actual + @@ -1,2 +1 @@ + -not the + same in both +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) +} + +type cmpFailure interface { + FailureMessage() string +} + +func TestMatchAnyFileMode(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("data", "content", + WithMode(0777))) + defer dir.Remove() + + expected := Expected(t, + WithFile("data", "content", MatchAnyFileMode)) + assert.Assert(t, Equal(dir.Path(), expected)) +} + +func TestMatchFileContent(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("data", "content")) + defer dir.Remove() + + t.Run("content matches", func(t *testing.T) { + matcher := func(b []byte) CompareResult { + return is.ResultSuccess + } + manifest := Expected(t, + WithFile("data", "different", MatchFileContent(matcher))) + assert.Assert(t, Equal(dir.Path(), manifest)) + }) + + t.Run("content does not match", func(t *testing.T) { + matcher := func(b []byte) CompareResult { + return is.ResultFailure("data content differs from expected") + } + manifest := Expected(t, + WithFile("data", "content", MatchFileContent(matcher))) + result := Equal(dir.Path(), manifest)() + assert.Assert(t, !result.Success()) + + expected := fmtExpected(`directory %s does not match expected: +/data + content: data content differs from expected +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) + }) +} + +func TestMatchExtraFilesGlob(t *testing.T) { + dir := NewDir(t, t.Name(), + WithFile("t.go", "data"), + WithFile("a.go", "data"), + WithFile("conf.yml", "content", WithMode(0600))) + defer dir.Remove() + + t.Run("matching globs", func(t *testing.T) { + manifest := Expected(t, + MatchFilesWithGlob("*.go", MatchAnyFileMode, MatchAnyFileContent), + MatchFilesWithGlob("*.yml", MatchAnyFileMode, MatchAnyFileContent)) + assert.Assert(t, Equal(dir.Path(), manifest)) + }) + + t.Run("matching globs with wrong mode", func(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "expect mode does not match on windows") + manifest := Expected(t, + MatchFilesWithGlob("*.go", MatchAnyFileMode, MatchAnyFileContent), + MatchFilesWithGlob("*.yml", MatchAnyFileContent, WithMode(0700))) + + result := Equal(dir.Path(), manifest)() + + assert.Assert(t, !result.Success()) + expected := fmtExpected(`directory %s does not match expected: +conf.yml + mode: expected -rwx------ got -rw------- +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) + }) + + t.Run("matching partial glob", func(t *testing.T) { + manifest := Expected(t, MatchFilesWithGlob("*.go", MatchAnyFileMode, MatchAnyFileContent)) + result := Equal(dir.Path(), manifest)() + assert.Assert(t, !result.Success()) + + expected := fmtExpected(`directory %s does not match expected: +/ + conf.yml: unexpected file +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) + }) + + t.Run("invalid glob", func(t *testing.T) { + manifest := Expected(t, MatchFilesWithGlob("[-x]")) + result := Equal(dir.Path(), manifest)() + assert.Assert(t, !result.Success()) + + expected := fmtExpected(`directory %s does not match expected: +/ + a.go: unexpected file + conf.yml: unexpected file + t.go: unexpected file +a.go + failed to match glob pattern: syntax error in pattern +conf.yml + failed to match glob pattern: syntax error in pattern +t.go + failed to match glob pattern: syntax error in pattern +`, dir.Path()) + assert.Equal(t, result.(cmpFailure).FailureMessage(), expected) + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/1 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/1 @@ -0,0 +1 @@ +1 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/1 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/1 @@ -0,0 +1 @@ +1 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/2 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/2 new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/2 @@ -0,0 +1 @@ +2 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/b/1 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/b/1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test-with-symlink/a/b/1 @@ -0,0 +1 @@ +1 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/1 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/1 @@ -0,0 +1 @@ +1 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/1 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/1 @@ -0,0 +1 @@ +1 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/2 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/2 new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/2 @@ -0,0 +1 @@ +2 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/b/1 b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/b/1 new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/fs/testdata/copy-test/a/b/1 @@ -0,0 +1 @@ +1 diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/go.mod b/root/pkg/mod/gotest.tools/v3@v3.3.0/go.mod new file mode 100644 index 0000000..abeca34 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/go.mod @@ -0,0 +1,10 @@ +module gotest.tools/v3 + +go 1.13 + +require ( + github.com/google/go-cmp v0.5.5 + github.com/spf13/pflag v1.0.3 + golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 + golang.org/x/tools v0.1.0 +) diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/go.sum b/root/pkg/mod/gotest.tools/v3@v3.3.0/go.sum new file mode 100644 index 0000000..ab53950 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/go.sum @@ -0,0 +1,31 @@ +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/example_test.go new file mode 100644 index 0000000..7f9847e --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/example_test.go @@ -0,0 +1,22 @@ +package golden_test + +import ( + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/golden" +) + +var t = &testing.T{} + +func ExampleAssert() { + golden.Assert(t, "foo", "foo-content.golden") +} + +func ExampleString() { + assert.Assert(t, golden.String("foo", "foo-content.golden")) +} + +func ExampleAssertBytes() { + golden.AssertBytes(t, []byte("foo"), "foo-content.golden") +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden.go new file mode 100644 index 0000000..47ea85f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden.go @@ -0,0 +1,190 @@ +/*Package golden provides tools for comparing large mutli-line strings. + +Golden files are files in the ./testdata/ subdirectory of the package under test. +Golden files can be automatically updated to match new values by running +`go test pkgname -update`. To ensure the update is correct +compare the diff of the old expected value to the new expected value. +*/ +package golden // import "gotest.tools/v3/golden" + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/format" + "gotest.tools/v3/internal/source" +) + +func init() { + flag.BoolVar(&source.Update, "test.update-golden", false, "deprecated flag") +} + +type helperT interface { + Helper() +} + +// NormalizeCRLFToLF enables end-of-line normalization for actual values passed +// to Assert and String, as well as the values saved to golden files with +// -update. +// +// Defaults to true. If you use the core.autocrlf=true git setting on windows +// you will need to set this to false. +// +// The value may be set to false by setting GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF=false +// in the environment before running tests. +// +// The default value may change in a future major release. +var NormalizeCRLFToLF = os.Getenv("GOTESTTOOLS_GOLDEN_NormalizeCRLFToLF") != "false" + +// FlagUpdate returns true when the -update flag has been set. +func FlagUpdate() bool { + return source.Update +} + +// Open opens the file in ./testdata +func Open(t assert.TestingT, filename string) *os.File { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + f, err := os.Open(Path(filename)) + assert.NilError(t, err) + return f +} + +// Get returns the contents of the file in ./testdata +func Get(t assert.TestingT, filename string) []byte { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + expected, err := ioutil.ReadFile(Path(filename)) + assert.NilError(t, err) + return expected +} + +// Path returns the full path to a file in ./testdata +func Path(filename string) string { + if filepath.IsAbs(filename) { + return filename + } + return filepath.Join("testdata", filename) +} + +func removeCarriageReturn(in []byte) []byte { + if !NormalizeCRLFToLF { + return in + } + return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) +} + +// Assert compares actual to the expected value in the golden file. +// +// Running `go test pkgname -update` will write the value of actual +// to the golden file. +// +// This is equivalent to assert.Assert(t, String(actual, filename)) +func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert.Assert(t, String(actual, filename), msgAndArgs...) +} + +// String compares actual to the contents of filename and returns success +// if the strings are equal. +// +// Running `go test pkgname -update` will write the value of actual +// to the golden file. +// +// Any \r\n substrings in actual are converted to a single \n character +// before comparing it to the expected string. When updating the golden file the +// normalized version will be written to the file. This allows Windows to use +// the same golden files as other operating systems. +func String(actual string, filename string) cmp.Comparison { + return func() cmp.Result { + actualBytes := removeCarriageReturn([]byte(actual)) + result, expected := compare(actualBytes, filename) + if result != nil { + return result + } + diff := format.UnifiedDiff(format.DiffConfig{ + A: string(expected), + B: string(actualBytes), + From: "expected", + To: "actual", + }) + return cmp.ResultFailure("\n" + diff + failurePostamble(filename)) + } +} + +func failurePostamble(filename string) string { + return fmt.Sprintf(` + +You can run 'go test . -update' to automatically update %s to the new expected value.' +`, Path(filename)) +} + +// AssertBytes compares actual to the expected value in the golden. +// +// Running `go test pkgname -update` will write the value of actual +// to the golden file. +// +// This is equivalent to assert.Assert(t, Bytes(actual, filename)) +func AssertBytes( + t assert.TestingT, + actual []byte, + filename string, + msgAndArgs ...interface{}, +) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert.Assert(t, Bytes(actual, filename), msgAndArgs...) +} + +// Bytes compares actual to the contents of filename and returns success +// if the bytes are equal. +// +// Running `go test pkgname -update` will write the value of actual +// to the golden file. +func Bytes(actual []byte, filename string) cmp.Comparison { + return func() cmp.Result { + result, expected := compare(actual, filename) + if result != nil { + return result + } + msg := fmt.Sprintf("%v (actual) != %v (expected)", actual, expected) + return cmp.ResultFailure(msg + failurePostamble(filename)) + } +} + +func compare(actual []byte, filename string) (cmp.Result, []byte) { + if err := update(filename, actual); err != nil { + return cmp.ResultFromError(err), nil + } + expected, err := ioutil.ReadFile(Path(filename)) + if err != nil { + return cmp.ResultFromError(err), nil + } + if bytes.Equal(expected, actual) { + return cmp.ResultSuccess, nil + } + return nil, expected +} + +func update(filename string, actual []byte) error { + if !source.Update { + return nil + } + if dir := filepath.Dir(Path(filename)); dir != "." { + if err := os.MkdirAll(dir, 0755); err != nil { + return err + } + } + return ioutil.WriteFile(Path(filename), actual, 0644) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden_test.go new file mode 100644 index 0000000..3b0bd02 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/golden/golden_test.go @@ -0,0 +1,296 @@ +package golden + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/fs" + "gotest.tools/v3/internal/source" +) + +type fakeT struct { + Failed bool +} + +func (t *fakeT) Log(...interface{}) { +} + +func (t *fakeT) FailNow() { + t.Failed = true +} + +func (t *fakeT) Fail() { + t.Failed = true +} + +func (t *fakeT) Helper() {} + +func TestGoldenOpenInvalidFile(t *testing.T) { + fakeT := new(fakeT) + + Open(fakeT, "/invalid/path") + assert.Assert(t, fakeT.Failed) +} + +func TestGoldenOpenAbsolutePath(t *testing.T) { + file := fs.NewFile(t, "abs-test", fs.WithContent("content\n")) + defer file.Remove() + fakeT := new(fakeT) + + f := Open(fakeT, file.Path()) + assert.Assert(t, !fakeT.Failed) + f.Close() +} + +func TestGoldenOpen(t *testing.T) { + filename, clean := setupGoldenFile(t, "") + defer clean() + + fakeT := new(fakeT) + + f := Open(fakeT, filename) + assert.Assert(t, !fakeT.Failed) + f.Close() +} + +func TestGoldenGetInvalidFile(t *testing.T) { + fakeT := new(fakeT) + + Get(fakeT, "/invalid/path") + assert.Assert(t, fakeT.Failed) +} + +func TestGoldenGetAbsolutePath(t *testing.T) { + file := fs.NewFile(t, "abs-test", fs.WithContent("content\n")) + defer file.Remove() + fakeT := new(fakeT) + + Get(fakeT, file.Path()) + assert.Assert(t, !fakeT.Failed) +} + +func TestGoldenGet(t *testing.T) { + expected := "content\nline1\nline2" + + filename, clean := setupGoldenFile(t, expected) + defer clean() + + fakeT := new(fakeT) + + actual := Get(fakeT, filename) + assert.Assert(t, !fakeT.Failed) + assert.Assert(t, cmp.DeepEqual(actual, []byte(expected))) +} + +func TestGoldenAssertInvalidContent(t *testing.T) { + filename, clean := setupGoldenFile(t, "content") + defer clean() + + fakeT := new(fakeT) + + Assert(fakeT, "foo", filename) + assert.Assert(t, fakeT.Failed) +} + +func TestGoldenAssertInvalidContentUpdate(t *testing.T) { + setUpdateFlag(t) + filename, clean := setupGoldenFile(t, "content") + defer clean() + + fakeT := new(fakeT) + + Assert(fakeT, "foo", filename) + assert.Assert(t, !fakeT.Failed) +} + +func TestGoldenAssertAbsolutePath(t *testing.T) { + file := fs.NewFile(t, "abs-test", fs.WithContent("foo")) + defer file.Remove() + fakeT := new(fakeT) + + Assert(fakeT, "foo", file.Path()) + assert.Assert(t, !fakeT.Failed) +} + +func TestGoldenAssertInDir(t *testing.T) { + filename, clean := setupGoldenFileWithDir(t, "testdatasubdir", "foo") + defer clean() + + fakeT := new(fakeT) + + Assert(fakeT, "foo", filepath.Join("testdatasubdir", filename)) + assert.Assert(t, !fakeT.Failed) + + _, err := os.Stat("testdatasubdir") + assert.Assert(t, os.IsNotExist(err), "testdatasubdir should not exist outside of testdata") +} + +func TestGoldenAssertInDir_UpdateGolden(t *testing.T) { + filename, clean := setupGoldenFileWithDir(t, "testdatasubdir", "foo") + defer clean() + setUpdateFlag(t) + + fakeT := new(fakeT) + + Assert(fakeT, "foo", filepath.Join("testdatasubdir", filename)) + assert.Assert(t, !fakeT.Failed) + + _, err := os.Stat("testdatasubdir") + assert.Assert(t, os.IsNotExist(err), "testdatasubdir should not exist outside of testdata") +} + +func TestGoldenAssert(t *testing.T) { + filename, clean := setupGoldenFile(t, "foo") + defer clean() + + fakeT := new(fakeT) + + Assert(fakeT, "foo", filename) + assert.Assert(t, !fakeT.Failed) +} + +func TestAssert_WithCarriageReturnInActual(t *testing.T) { + filename, clean := setupGoldenFile(t, "a\rfoo\nbar\n") + defer clean() + + fakeT := new(fakeT) + + Assert(fakeT, "a\rfoo\r\nbar\r\n", filename) + assert.Assert(t, !fakeT.Failed) +} + +func TestAssert_WithCarriageReturnInActual_UpdateGolden(t *testing.T) { + filename, clean := setupGoldenFile(t, "") + defer clean() + unsetUpdateFlag := setUpdateFlag(t) + + fakeT := new(fakeT) + Assert(fakeT, "a\rfoo\r\nbar\r\n", filename) + assert.Assert(t, !fakeT.Failed) + + unsetUpdateFlag() + actual := Get(fakeT, filename) + assert.Equal(t, string(actual), "a\rfoo\nbar\n") + + Assert(t, "a\rfoo\r\nbar\r\n", filename, "matches with carriage returns") + Assert(t, "a\rfoo\nbar\n", filename, "matches without carriage returns") +} + +func TestGoldenAssertBytes(t *testing.T) { + filename, clean := setupGoldenFile(t, "foo") + defer clean() + + fakeT := new(fakeT) + + AssertBytes(fakeT, []byte("foo"), filename) + assert.Assert(t, !fakeT.Failed) +} + +func setUpdateFlag(t *testing.T) func() { + orig := source.Update + source.Update = true + undo := func() { + source.Update = orig + } + t.Cleanup(undo) + return undo +} + +func setupGoldenFileWithDir(t *testing.T, dirname, content string) (string, func()) { + dirpath := filepath.Join("testdata", dirname) + _ = os.MkdirAll(filepath.Join("testdata", dirname), 0755) + f, err := ioutil.TempFile(dirpath, t.Name()+"-") + assert.NilError(t, err, "fail to create test golden file") + defer f.Close() + + _, err = f.Write([]byte(content)) + assert.NilError(t, err) + + return filepath.Base(f.Name()), func() { + assert.NilError(t, os.Remove(f.Name())) + assert.NilError(t, os.Remove(dirpath)) + } +} + +func setupGoldenFile(t *testing.T, content string) (string, func()) { + _ = os.Mkdir("testdata", 0755) + f, err := ioutil.TempFile("testdata", t.Name()+"-") + assert.NilError(t, err, "fail to create test golden file") + defer f.Close() + + _, err = f.Write([]byte(content)) + assert.NilError(t, err) + + return filepath.Base(f.Name()), func() { + assert.NilError(t, os.Remove(f.Name())) + } +} + +func TestStringFailure(t *testing.T) { + filename, clean := setupGoldenFile(t, "this is\nthe text") + defer clean() + + result := String("this is\nnot the text", filename)() + assert.Assert(t, !result.Success()) + assert.Equal(t, result.(failure).FailureMessage(), ` +--- expected ++++ actual +@@ -1,2 +1,2 @@ + this is +-the text ++not the text +`+failurePostamble(filename)) +} + +type failure interface { + FailureMessage() string +} + +func TestBytesFailure(t *testing.T) { + filename, clean := setupGoldenFile(t, "5556") + defer clean() + + result := Bytes([]byte("5555"), filename)() + assert.Assert(t, !result.Success()) + assert.Equal(t, result.(failure).FailureMessage(), + `[53 53 53 53] (actual) != [53 53 53 54] (expected)`+failurePostamble(filename)) +} + +func TestFlagUpdate(t *testing.T) { + assert.Assert(t, !FlagUpdate()) + setUpdateFlag(t) + assert.Assert(t, FlagUpdate()) +} + +func TestUpdate_CreatesPathsAndFile(t *testing.T) { + setUpdateFlag(t) + + dir := fs.NewDir(t, t.Name()) + + t.Run("creates the file", func(t *testing.T) { + filename := dir.Join("filename") + err := update(filename, nil) + assert.NilError(t, err) + + _, err = os.Stat(filename) + assert.NilError(t, err) + }) + + t.Run("creates directories", func(t *testing.T) { + filename := dir.Join("one/two/filename") + err := update(filename, nil) + assert.NilError(t, err) + + _, err = os.Stat(filename) + assert.NilError(t, err) + + t.Run("no error when directory exists", func(t *testing.T) { + err = update(filename, nil) + assert.NilError(t, err) + }) + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command.go new file mode 100644 index 0000000..9613322 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command.go @@ -0,0 +1,290 @@ +/*Package icmd executes binaries and provides convenient assertions for testing the results. + */ +package icmd // import "gotest.tools/v3/icmd" + +import ( + "bytes" + "fmt" + "io" + "os" + "strings" + "sync" + "time" + + exec "golang.org/x/sys/execabs" + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +type helperT interface { + Helper() +} + +// None is a token to inform Result.Assert that the output should be empty +const None = "[NOTHING]" + +type lockedBuffer struct { + m sync.RWMutex + buf bytes.Buffer +} + +func (buf *lockedBuffer) Write(b []byte) (int, error) { + buf.m.Lock() + defer buf.m.Unlock() + return buf.buf.Write(b) +} + +func (buf *lockedBuffer) String() string { + buf.m.RLock() + defer buf.m.RUnlock() + return buf.buf.String() +} + +// Result stores the result of running a command +type Result struct { + Cmd *exec.Cmd + ExitCode int + Error error + // Timeout is true if the command was killed because it ran for too long + Timeout bool + outBuffer *lockedBuffer + errBuffer *lockedBuffer +} + +// Assert compares the Result against the Expected struct, and fails the test if +// any of the expectations are not met. +// +// This function is equivalent to assert.Assert(t, result.Equal(exp)). +func (r *Result) Assert(t assert.TestingT, exp Expected) *Result { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert.Assert(t, r.Equal(exp)) + return r +} + +// Equal compares the result to Expected. If the result doesn't match expected +// returns a formatted failure message with the command, stdout, stderr, exit code, +// and any failed expectations. +func (r *Result) Equal(exp Expected) cmp.Comparison { + return func() cmp.Result { + return cmp.ResultFromError(r.match(exp)) + } +} + +// Compare the result to Expected and return an error if they do not match. +func (r *Result) Compare(exp Expected) error { + return r.match(exp) +} + +func (r *Result) match(exp Expected) error { + errors := []string{} + add := func(format string, args ...interface{}) { + errors = append(errors, fmt.Sprintf(format, args...)) + } + + if exp.ExitCode != r.ExitCode { + add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode) + } + if exp.Timeout != r.Timeout { + if exp.Timeout { + add("Expected command to timeout") + } else { + add("Expected command to finish, but it hit the timeout") + } + } + if !matchOutput(exp.Out, r.Stdout()) { + add("Expected stdout to contain %q", exp.Out) + } + if !matchOutput(exp.Err, r.Stderr()) { + add("Expected stderr to contain %q", exp.Err) + } + switch { + // If a non-zero exit code is expected there is going to be an error. + // Don't require an error message as well as an exit code because the + // error message is going to be "exit status which is not useful + case exp.Error == "" && exp.ExitCode != 0: + case exp.Error == "" && r.Error != nil: + add("Expected no error") + case exp.Error != "" && r.Error == nil: + add("Expected error to contain %q, but there was no error", exp.Error) + case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error): + add("Expected error to contain %q", exp.Error) + } + + if len(errors) == 0 { + return nil + } + return fmt.Errorf("%s\nFailures:\n%s", r, strings.Join(errors, "\n")) +} + +func matchOutput(expected string, actual string) bool { + switch expected { + case None: + return actual == "" + default: + return strings.Contains(actual, expected) + } +} + +func (r *Result) String() string { + var timeout string + if r.Timeout { + timeout = " (timeout)" + } + var errString string + if r.Error != nil { + errString = "\nError: " + r.Error.Error() + } + + return fmt.Sprintf(` +Command: %s +ExitCode: %d%s%s +Stdout: %v +Stderr: %v +`, + strings.Join(r.Cmd.Args, " "), + r.ExitCode, + timeout, + errString, + r.Stdout(), + r.Stderr()) +} + +// Expected is the expected output from a Command. This struct is compared to a +// Result struct by Result.Assert(). +type Expected struct { + ExitCode int + Timeout bool + Error string + Out string + Err string +} + +// Success is the default expected result. A Success result is one with a 0 +// ExitCode. +var Success = Expected{} + +// Stdout returns the stdout of the process as a string +func (r *Result) Stdout() string { + return r.outBuffer.String() +} + +// Stderr returns the stderr of the process as a string +func (r *Result) Stderr() string { + return r.errBuffer.String() +} + +// Combined returns the stdout and stderr combined into a single string +func (r *Result) Combined() string { + return r.outBuffer.String() + r.errBuffer.String() +} + +func (r *Result) setExitError(err error) { + if err == nil { + return + } + r.Error = err + r.ExitCode = processExitCode(err) +} + +// Cmd contains the arguments and options for a process to run as part of a test +// suite. +type Cmd struct { + Command []string + Timeout time.Duration + Stdin io.Reader + Stdout io.Writer + Dir string + Env []string + ExtraFiles []*os.File +} + +// Command create a simple Cmd with the specified command and arguments +func Command(command string, args ...string) Cmd { + return Cmd{Command: append([]string{command}, args...)} +} + +// RunCmd runs a command and returns a Result +func RunCmd(cmd Cmd, cmdOperators ...CmdOp) *Result { + for _, op := range cmdOperators { + op(&cmd) + } + result := StartCmd(cmd) + if result.Error != nil { + return result + } + return WaitOnCmd(cmd.Timeout, result) +} + +// RunCommand runs a command with default options, and returns a result +func RunCommand(command string, args ...string) *Result { + return RunCmd(Command(command, args...)) +} + +// StartCmd starts a command, but doesn't wait for it to finish +func StartCmd(cmd Cmd) *Result { + result := buildCmd(cmd) + if result.Error != nil { + return result + } + result.setExitError(result.Cmd.Start()) + return result +} + +// TODO: support exec.CommandContext +func buildCmd(cmd Cmd) *Result { + var execCmd *exec.Cmd + switch len(cmd.Command) { + case 1: + execCmd = exec.Command(cmd.Command[0]) + default: + execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...) + } + outBuffer := new(lockedBuffer) + errBuffer := new(lockedBuffer) + + execCmd.Stdin = cmd.Stdin + execCmd.Dir = cmd.Dir + execCmd.Env = cmd.Env + if cmd.Stdout != nil { + execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout) + } else { + execCmd.Stdout = outBuffer + } + execCmd.Stderr = errBuffer + execCmd.ExtraFiles = cmd.ExtraFiles + + return &Result{ + Cmd: execCmd, + outBuffer: outBuffer, + errBuffer: errBuffer, + } +} + +// WaitOnCmd waits for a command to complete. If timeout is non-nil then +// only wait until the timeout. +func WaitOnCmd(timeout time.Duration, result *Result) *Result { + if timeout == time.Duration(0) { + result.setExitError(result.Cmd.Wait()) + return result + } + + done := make(chan error, 1) + // Wait for command to exit in a goroutine + go func() { + done <- result.Cmd.Wait() + }() + + select { + case <-time.After(timeout): + killErr := result.Cmd.Process.Kill() + if killErr != nil { + fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr) + } + result.Timeout = true + case err := <-done: + result.setExitError(err) + } + return result +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go new file mode 100644 index 0000000..1a5fef9 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/command_test.go @@ -0,0 +1,181 @@ +package icmd + +import ( + "bytes" + "errors" + "os" + "path/filepath" + "runtime" + "testing" + "time" + + exec "golang.org/x/sys/execabs" + "gotest.tools/v3/assert" + "gotest.tools/v3/fs" + "gotest.tools/v3/internal/maint" +) + +var ( + bindir = fs.NewDir(maint.T, "icmd-dir") + binname = bindir.Join("bin-stub") + pathext() + stubpath = filepath.FromSlash("./internal/stub") +) + +func pathext() string { + if runtime.GOOS == "windows" { + return ".exe" + } + return "" +} + +func TestMain(m *testing.M) { + exitcode := m.Run() + bindir.Remove() + os.Exit(exitcode) +} + +func buildStub(t assert.TestingT) { + if _, err := os.Stat(binname); err == nil { + return + } + result := RunCommand("go", "build", "-o", binname, stubpath) + result.Assert(t, Success) +} + +func TestRunCommandSuccess(t *testing.T) { + buildStub(t) + + result := RunCommand(binname) + result.Assert(t, Success) +} + +func TestRunCommandWithCombined(t *testing.T) { + buildStub(t) + + result := RunCommand(binname, "-warn") + result.Assert(t, Expected{}) + + assert.Equal(t, result.Combined(), "this is stdout\nthis is stderr\n") + assert.Equal(t, result.Stdout(), "this is stdout\n") + assert.Equal(t, result.Stderr(), "this is stderr\n") +} + +func TestRunCommandWithTimeoutFinished(t *testing.T) { + buildStub(t) + + result := RunCmd(Cmd{ + Command: []string{binname, "-sleep=1ms"}, + Timeout: 2 * time.Second, + }) + result.Assert(t, Expected{Out: "this is stdout"}) +} + +func TestRunCommandWithTimeoutKilled(t *testing.T) { + buildStub(t) + + command := []string{binname, "-sleep=200ms"} + result := RunCmd(Cmd{Command: command, Timeout: 30 * time.Millisecond}) + result.Assert(t, Expected{Timeout: true, Out: None, Err: None}) +} + +func TestRunCommandWithErrors(t *testing.T) { + buildStub(t) + + result := RunCommand("doesnotexists") + expected := `exec: "doesnotexists": executable file not found` + result.Assert(t, Expected{Out: None, Err: None, ExitCode: 127, Error: expected}) +} + +func TestRunCommandWithStdoutNoStderr(t *testing.T) { + buildStub(t) + + result := RunCommand(binname) + result.Assert(t, Expected{Out: "this is stdout\n", Err: None}) +} + +func TestRunCommandWithExitCode(t *testing.T) { + buildStub(t) + + result := RunCommand(binname, "-fail=99") + result.Assert(t, Expected{ + ExitCode: 99, + Error: "exit status 99", + }) +} + +func TestResult_Match_NotMatched(t *testing.T) { + result := &Result{ + Cmd: exec.Command("binary", "arg1"), + ExitCode: 99, + Error: errors.New("exit code 99"), + outBuffer: newLockedBuffer("the output"), + errBuffer: newLockedBuffer("the stderr"), + Timeout: true, + } + exp := Expected{ + ExitCode: 101, + Out: "Something else", + Err: None, + } + err := result.match(exp) + assert.ErrorContains(t, err, "Failures") + assert.Equal(t, err.Error(), expectedMatch) +} + +var expectedMatch = ` +Command: binary arg1 +ExitCode: 99 (timeout) +Error: exit code 99 +Stdout: the output +Stderr: the stderr + +Failures: +ExitCode was 99 expected 101 +Expected command to finish, but it hit the timeout +Expected stdout to contain "Something else" +Expected stderr to contain "[NOTHING]"` + +func newLockedBuffer(s string) *lockedBuffer { + return &lockedBuffer{buf: *bytes.NewBufferString(s)} +} + +func TestResult_Match_NotMatchedNoError(t *testing.T) { + result := &Result{ + Cmd: exec.Command("binary", "arg1"), + outBuffer: newLockedBuffer("the output"), + errBuffer: newLockedBuffer("the stderr"), + } + exp := Expected{ + ExitCode: 101, + Out: "Something else", + Err: None, + } + err := result.match(exp) + assert.ErrorContains(t, err, "Failures") + assert.Equal(t, err.Error(), expectedResultMatchNoMatch) +} + +var expectedResultMatchNoMatch = ` +Command: binary arg1 +ExitCode: 0 +Stdout: the output +Stderr: the stderr + +Failures: +ExitCode was 0 expected 101 +Expected stdout to contain "Something else" +Expected stderr to contain "[NOTHING]"` + +func TestResult_Match_Match(t *testing.T) { + result := &Result{ + Cmd: exec.Command("binary", "arg1"), + outBuffer: newLockedBuffer("the output"), + errBuffer: newLockedBuffer("the stderr"), + } + exp := Expected{ + Out: "the output", + Err: "the stderr", + } + err := result.match(exp) + assert.NilError(t, err) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go new file mode 100644 index 0000000..216b82d --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/example_test.go @@ -0,0 +1,22 @@ +package icmd_test + +import ( + "testing" + + "gotest.tools/v3/icmd" +) + +var t = &testing.T{} + +func ExampleRunCommand() { + result := icmd.RunCommand("bash", "-c", "echo all good") + result.Assert(t, icmd.Success) +} + +func ExampleRunCmd() { + result := icmd.RunCmd(icmd.Command("cat", "/does/not/exist")) + result.Assert(t, icmd.Expected{ + ExitCode: 1, + Err: "cat: /does/not/exist: No such file or directory", + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go new file mode 100644 index 0000000..2e98f86 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/exitcode.go @@ -0,0 +1,24 @@ +package icmd + +import ( + "errors" + + exec "golang.org/x/sys/execabs" +) + +func processExitCode(err error) int { + if err == nil { + return 0 + } + + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + if exitErr.ProcessState == nil { + return 0 + } + if code := exitErr.ProcessState.ExitCode(); code != -1 { + return code + } + } + return 127 +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go new file mode 100644 index 0000000..a7fa1fd --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/internal/stub/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "flag" + "fmt" + "os" + "time" +) + +func main() { + sleep := flag.Duration("sleep", 0, "Sleep") + warn := flag.Bool("warn", false, "Warn") + fail := flag.Int("fail", 0, "Fail with code") + flag.Parse() + + if *sleep != 0 { + time.Sleep(*sleep) + } + + fmt.Println("this is stdout") + if *warn { + fmt.Fprintln(os.Stderr, "this is stderr") + } + + os.Exit(*fail) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go new file mode 100644 index 0000000..35c3958 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/icmd/ops.go @@ -0,0 +1,46 @@ +package icmd + +import ( + "io" + "os" + "time" +) + +// CmdOp is an operation which modified a Cmd structure used to execute commands +type CmdOp func(*Cmd) + +// WithTimeout sets the timeout duration of the command +func WithTimeout(timeout time.Duration) CmdOp { + return func(c *Cmd) { + c.Timeout = timeout + } +} + +// WithEnv sets the environment variable of the command. +// Each arguments are in the form of KEY=VALUE +func WithEnv(env ...string) CmdOp { + return func(c *Cmd) { + c.Env = env + } +} + +// Dir sets the working directory of the command +func Dir(path string) CmdOp { + return func(c *Cmd) { + c.Dir = path + } +} + +// WithStdin sets the standard input of the command to the specified reader +func WithStdin(r io.Reader) CmdOp { + return func(c *Cmd) { + c.Stdin = r + } +} + +// WithExtraFile adds a file descriptor to the command +func WithExtraFile(f *os.File) CmdOp { + return func(c *Cmd) { + c.ExtraFiles = append(c.ExtraFiles, f) + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go new file mode 100644 index 0000000..0d67751 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/assert.go @@ -0,0 +1,160 @@ +package assert + +import ( + "fmt" + "go/ast" + "go/token" + "reflect" + + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/format" + "gotest.tools/v3/internal/source" +) + +// LogT is the subset of testing.T used by the assert package. +type LogT interface { + Log(args ...interface{}) +} + +type helperT interface { + Helper() +} + +const failureMessage = "assertion failed: " + +// Eval the comparison and print a failure messages if the comparison has failed. +func Eval( + t LogT, + argSelector argSelector, + comparison interface{}, + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + var success bool + switch check := comparison.(type) { + case bool: + if check { + return true + } + logFailureFromBool(t, msgAndArgs...) + + // Undocumented legacy comparison without Result type + case func() (success bool, message string): + success = runCompareFunc(t, check, msgAndArgs...) + + case nil: + return true + + case error: + msg := failureMsgFromError(check) + t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) + + case cmp.Comparison: + success = RunComparison(t, argSelector, check, msgAndArgs...) + + case func() cmp.Result: + success = RunComparison(t, argSelector, check, msgAndArgs...) + + default: + t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check)) + } + return success +} + +func runCompareFunc( + t LogT, + f func() (success bool, message string), + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if success, message := f(); !success { + t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) + return false + } + return true +} + +func logFailureFromBool(t LogT, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + const stackIndex = 3 // Assert()/Check(), assert(), logFailureFromBool() + args, err := source.CallExprArgs(stackIndex) + if err != nil { + t.Log(err.Error()) + return + } + + const comparisonArgIndex = 1 // Assert(t, comparison) + if len(args) <= comparisonArgIndex { + t.Log(failureMessage + "but assert failed to find the expression to print") + return + } + + msg, err := boolFailureMessage(args[comparisonArgIndex]) + if err != nil { + t.Log(err.Error()) + msg = "expression is false" + } + + t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) +} + +func failureMsgFromError(err error) string { + // Handle errors with non-nil types + v := reflect.ValueOf(err) + if v.Kind() == reflect.Ptr && v.IsNil() { + return fmt.Sprintf("error is not nil: error has type %T", err) + } + return "error is not nil: " + err.Error() +} + +func boolFailureMessage(expr ast.Expr) (string, error) { + if binaryExpr, ok := expr.(*ast.BinaryExpr); ok { + x, err := source.FormatNode(binaryExpr.X) + if err != nil { + return "", err + } + y, err := source.FormatNode(binaryExpr.Y) + if err != nil { + return "", err + } + + switch binaryExpr.Op { + case token.NEQ: + return x + " is " + y, nil + case token.EQL: + return x + " is not " + y, nil + case token.GTR: + return x + " is <= " + y, nil + case token.LSS: + return x + " is >= " + y, nil + case token.GEQ: + return x + " is less than " + y, nil + case token.LEQ: + return x + " is greater than " + y, nil + } + } + + if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT { + x, err := source.FormatNode(unaryExpr.X) + if err != nil { + return "", err + } + return x + " is true", nil + } + + if ident, ok := expr.(*ast.Ident); ok { + return ident.Name + " is false", nil + } + + formatted, err := source.FormatNode(expr) + if err != nil { + return "", err + } + return "expression is false: " + formatted, nil +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go new file mode 100644 index 0000000..3603206 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/assert/result.go @@ -0,0 +1,146 @@ +package assert + +import ( + "errors" + "fmt" + "go/ast" + + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/format" + "gotest.tools/v3/internal/source" +) + +// RunComparison and return Comparison.Success. If the comparison fails a messages +// will be printed using t.Log. +func RunComparison( + t LogT, + argSelector argSelector, + f cmp.Comparison, + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + result := f() + if result.Success() { + return true + } + + if source.Update { + if updater, ok := result.(updateExpected); ok { + const stackIndex = 3 // Assert/Check, assert, RunComparison + err := updater.UpdatedExpected(stackIndex) + switch { + case err == nil: + return true + case errors.Is(err, source.ErrNotFound): + // do nothing, fallthrough to regular failure message + default: + t.Log("failed to update source", err) + return false + } + } + } + + var message string + switch typed := result.(type) { + case resultWithComparisonArgs: + const stackIndex = 3 // Assert/Check, assert, RunComparison + args, err := source.CallExprArgs(stackIndex) + if err != nil { + t.Log(err.Error()) + } + message = typed.FailureMessage(filterPrintableExpr(argSelector(args))) + case resultBasic: + message = typed.FailureMessage() + default: + message = fmt.Sprintf("comparison returned invalid Result type: %T", result) + } + + t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) + return false +} + +type resultWithComparisonArgs interface { + FailureMessage(args []ast.Expr) string +} + +type resultBasic interface { + FailureMessage() string +} + +type updateExpected interface { + UpdatedExpected(stackIndex int) error +} + +// filterPrintableExpr filters the ast.Expr slice to only include Expr that are +// easy to read when printed and contain relevant information to an assertion. +// +// Ident and SelectorExpr are included because they print nicely and the variable +// names may provide additional context to their values. +// BasicLit and CompositeLit are excluded because their source is equivalent to +// their value, which is already available. +// Other types are ignored for now, but could be added if they are relevant. +func filterPrintableExpr(args []ast.Expr) []ast.Expr { + result := make([]ast.Expr, len(args)) + for i, arg := range args { + if isShortPrintableExpr(arg) { + result[i] = arg + continue + } + + if starExpr, ok := arg.(*ast.StarExpr); ok { + result[i] = starExpr.X + continue + } + } + return result +} + +func isShortPrintableExpr(expr ast.Expr) bool { + switch expr.(type) { + case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr: + return true + case *ast.BinaryExpr, *ast.UnaryExpr: + return true + default: + // CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr + return false + } +} + +type argSelector func([]ast.Expr) []ast.Expr + +// ArgsAfterT selects args starting at position 1. Used when the caller has a +// testing.T as the first argument, and the args to select should follow it. +func ArgsAfterT(args []ast.Expr) []ast.Expr { + if len(args) < 1 { + return nil + } + return args[1:] +} + +// ArgsFromComparisonCall selects args from the CallExpression at position 1. +// Used when the caller has a testing.T as the first argument, and the args to +// select are passed to the cmp.Comparison at position 1. +func ArgsFromComparisonCall(args []ast.Expr) []ast.Expr { + if len(args) <= 1 { + return nil + } + if callExpr, ok := args[1].(*ast.CallExpr); ok { + return callExpr.Args + } + return nil +} + +// ArgsAtZeroIndex selects args from the CallExpression at position 1. +// Used when the caller accepts a single cmp.Comparison argument. +func ArgsAtZeroIndex(args []ast.Expr) []ast.Expr { + if len(args) == 0 { + return nil + } + if callExpr, ok := args[0].(*ast.CallExpr); ok { + return callExpr.Args + } + return nil +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go new file mode 100644 index 0000000..58206e5 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/cleanup/cleanup.go @@ -0,0 +1,48 @@ +/*Package cleanup handles migration to and support for the Go 1.14+ +testing.TB.Cleanup() function. +*/ +package cleanup + +import ( + "os" + "strings" +) + +type cleanupT interface { + Cleanup(f func()) +} + +// implemented by gotest.tools/x/subtest.TestContext +type addCleanupT interface { + AddCleanup(f func()) +} + +type logT interface { + Log(...interface{}) +} + +type helperT interface { + Helper() +} + +var noCleanup = strings.ToLower(os.Getenv("TEST_NOCLEANUP")) == "true" + +// Cleanup registers f as a cleanup function on t if any mechanisms are available. +// +// Skips registering f if TEST_NOCLEANUP is set to true. +func Cleanup(t logT, f func()) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if noCleanup { + t.Log("skipping cleanup because TEST_NOCLEANUP was enabled.") + return + } + if ct, ok := t.(cleanupT); ok { + ct.Cleanup(f) + return + } + if tc, ok := t.(addCleanupT); ok { + tc.AddCleanup(f) + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE new file mode 100644 index 0000000..c67dad6 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013, Patrick Mezard +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + The names of its contributors may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go new file mode 100644 index 0000000..9bf506b --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/difflib/difflib.go @@ -0,0 +1,423 @@ +/*Package difflib is a partial port of Python difflib module. + +Original source: https://github.com/pmezard/go-difflib + +This file is trimmed to only the parts used by this repository. +*/ +package difflib // import "gotest.tools/v3/internal/difflib" + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// Match stores line numbers of size of match +type Match struct { + A int + B int + Size int +} + +// OpCode identifies the type of diff +type OpCode struct { + Tag byte + I1 int + I2 int + J1 int + J2 int +} + +// SequenceMatcher compares sequence of strings. The basic +// algorithm predates, and is a little fancier than, an algorithm +// published in the late 1980's by Ratcliff and Obershelp under the +// hyperbolic name "gestalt pattern matching". The basic idea is to find +// the longest contiguous matching subsequence that contains no "junk" +// elements (R-O doesn't address junk). The same idea is then applied +// recursively to the pieces of the sequences to the left and to the right +// of the matching subsequence. This does not yield minimal edit +// sequences, but does tend to yield matches that "look right" to people. +// +// SequenceMatcher tries to compute a "human-friendly diff" between two +// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the +// longest *contiguous* & junk-free matching subsequence. That's what +// catches peoples' eyes. The Windows(tm) windiff has another interesting +// notion, pairing up elements that appear uniquely in each sequence. +// That, and the method here, appear to yield more intuitive difference +// reports than does diff. This method appears to be the least vulnerable +// to synching up on blocks of "junk lines", though (like blank lines in +// ordinary text files, or maybe "

" lines in HTML files). That may be +// because this is the only method of the 3 that has a *concept* of +// "junk" . +// +// Timing: Basic R-O is cubic time worst case and quadratic time expected +// case. SequenceMatcher is quadratic time for the worst case and has +// expected-case behavior dependent in a complicated way on how many +// elements the sequences have in common; best case time is linear. +type SequenceMatcher struct { + a []string + b []string + b2j map[string][]int + IsJunk func(string) bool + autoJunk bool + bJunk map[string]struct{} + matchingBlocks []Match + fullBCount map[string]int + bPopular map[string]struct{} + opCodes []OpCode +} + +// NewMatcher returns a new SequenceMatcher +func NewMatcher(a, b []string) *SequenceMatcher { + m := SequenceMatcher{autoJunk: true} + m.SetSeqs(a, b) + return &m +} + +// SetSeqs sets two sequences to be compared. +func (m *SequenceMatcher) SetSeqs(a, b []string) { + m.SetSeq1(a) + m.SetSeq2(b) +} + +// SetSeq1 sets the first sequence to be compared. The second sequence to be compared is +// not changed. +// +// SequenceMatcher computes and caches detailed information about the second +// sequence, so if you want to compare one sequence S against many sequences, +// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other +// sequences. +// +// See also SetSeqs() and SetSeq2(). +func (m *SequenceMatcher) SetSeq1(a []string) { + if &a == &m.a { + return + } + m.a = a + m.matchingBlocks = nil + m.opCodes = nil +} + +// SetSeq2 sets the second sequence to be compared. The first sequence to be compared is +// not changed. +func (m *SequenceMatcher) SetSeq2(b []string) { + if &b == &m.b { + return + } + m.b = b + m.matchingBlocks = nil + m.opCodes = nil + m.fullBCount = nil + m.chainB() +} + +func (m *SequenceMatcher) chainB() { + // Populate line -> index mapping + b2j := map[string][]int{} + for i, s := range m.b { + indices := b2j[s] + indices = append(indices, i) + b2j[s] = indices + } + + // Purge junk elements + m.bJunk = map[string]struct{}{} + if m.IsJunk != nil { + junk := m.bJunk + for s := range b2j { + if m.IsJunk(s) { + junk[s] = struct{}{} + } + } + for s := range junk { + delete(b2j, s) + } + } + + // Purge remaining popular elements + popular := map[string]struct{}{} + n := len(m.b) + if m.autoJunk && n >= 200 { + ntest := n/100 + 1 + for s, indices := range b2j { + if len(indices) > ntest { + popular[s] = struct{}{} + } + } + for s := range popular { + delete(b2j, s) + } + } + m.bPopular = popular + m.b2j = b2j +} + +func (m *SequenceMatcher) isBJunk(s string) bool { + _, ok := m.bJunk[s] + return ok +} + +// Find longest matching block in a[alo:ahi] and b[blo:bhi]. +// +// If IsJunk is not defined: +// +// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi +// and for all (i',j',k') meeting those conditions, +// k >= k' +// i <= i' +// and if i == i', j <= j' +// +// In other words, of all maximal matching blocks, return one that +// starts earliest in a, and of all those maximal matching blocks that +// start earliest in a, return the one that starts earliest in b. +// +// If IsJunk is defined, first the longest matching block is +// determined as above, but with the additional restriction that no +// junk element appears in the block. Then that block is extended as +// far as possible by matching (only) junk elements on both sides. So +// the resulting block never matches on junk except as identical junk +// happens to be adjacent to an "interesting" match. +// +// If no blocks match, return (alo, blo, 0). +func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { + // CAUTION: stripping common prefix or suffix would be incorrect. + // E.g., + // ab + // acab + // Longest matching block is "ab", but if common prefix is + // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so + // strip, so ends up claiming that ab is changed to acab by + // inserting "ca" in the middle. That's minimal but unintuitive: + // "it's obvious" that someone inserted "ac" at the front. + // Windiff ends up at the same place as diff, but by pairing up + // the unique 'b's and then matching the first two 'a's. + besti, bestj, bestsize := alo, blo, 0 + + // find longest junk-free match + // during an iteration of the loop, j2len[j] = length of longest + // junk-free match ending with a[i-1] and b[j] + j2len := map[int]int{} + for i := alo; i != ahi; i++ { + // look at all instances of a[i] in b; note that because + // b2j has no junk keys, the loop is skipped if a[i] is junk + newj2len := map[int]int{} + for _, j := range m.b2j[m.a[i]] { + // a[i] matches b[j] + if j < blo { + continue + } + if j >= bhi { + break + } + k := j2len[j-1] + 1 + newj2len[j] = k + if k > bestsize { + besti, bestj, bestsize = i-k+1, j-k+1, k + } + } + j2len = newj2len + } + + // Extend the best by non-junk elements on each end. In particular, + // "popular" non-junk elements aren't in b2j, which greatly speeds + // the inner loop above, but also means "the best" match so far + // doesn't contain any junk *or* popular non-junk elements. + for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + !m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + // Now that we have a wholly interesting match (albeit possibly + // empty!), we may as well suck up the matching junk on each + // side of it too. Can't think of a good reason not to, and it + // saves post-processing the (possibly considerable) expense of + // figuring out what to do with it. In the case of an empty + // interesting match, this is clearly the right thing to do, + // because no other kind of match is possible in the regions. + for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + return Match{A: besti, B: bestj, Size: bestsize} +} + +// GetMatchingBlocks returns a list of triples describing matching subsequences. +// +// Each triple is of the form (i, j, n), and means that +// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in +// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are +// adjacent triples in the list, and the second is not the last triple in the +// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe +// adjacent equal blocks. +// +// The last triple is a dummy, (len(a), len(b), 0), and is the only +// triple with n==0. +func (m *SequenceMatcher) GetMatchingBlocks() []Match { + if m.matchingBlocks != nil { + return m.matchingBlocks + } + + var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match + matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { + match := m.findLongestMatch(alo, ahi, blo, bhi) + i, j, k := match.A, match.B, match.Size + if match.Size > 0 { + if alo < i && blo < j { + matched = matchBlocks(alo, i, blo, j, matched) + } + matched = append(matched, match) + if i+k < ahi && j+k < bhi { + matched = matchBlocks(i+k, ahi, j+k, bhi, matched) + } + } + return matched + } + matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) + + // It's possible that we have adjacent equal blocks in the + // matching_blocks list now. + nonAdjacent := []Match{} + i1, j1, k1 := 0, 0, 0 + for _, b := range matched { + // Is this block adjacent to i1, j1, k1? + i2, j2, k2 := b.A, b.B, b.Size + if i1+k1 == i2 && j1+k1 == j2 { + // Yes, so collapse them -- this just increases the length of + // the first block by the length of the second, and the first + // block so lengthened remains the block to compare against. + k1 += k2 + } else { + // Not adjacent. Remember the first block (k1==0 means it's + // the dummy we started with), and make the second block the + // new block to compare against. + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + i1, j1, k1 = i2, j2, k2 + } + } + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + + nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) + m.matchingBlocks = nonAdjacent + return m.matchingBlocks +} + +// GetOpCodes returns a list of 5-tuples describing how to turn a into b. +// +// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple +// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the +// tuple preceding it, and likewise for j1 == the previous j2. +// +// The tags are characters, with these meanings: +// +// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] +// +// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. +// +// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. +// +// 'e' (equal): a[i1:i2] == b[j1:j2] +func (m *SequenceMatcher) GetOpCodes() []OpCode { + if m.opCodes != nil { + return m.opCodes + } + i, j := 0, 0 + matching := m.GetMatchingBlocks() + opCodes := make([]OpCode, 0, len(matching)) + for _, m := range matching { + // invariant: we've pumped out correct diffs to change + // a[:i] into b[:j], and the next matching block is + // a[ai:ai+size] == b[bj:bj+size]. So we need to pump + // out a diff to change a[i:ai] into b[j:bj], pump out + // the matching block, and move (i,j) beyond the match + ai, bj, size := m.A, m.B, m.Size + tag := byte(0) + if i < ai && j < bj { + tag = 'r' + } else if i < ai { + tag = 'd' + } else if j < bj { + tag = 'i' + } + if tag > 0 { + opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) + } + i, j = ai+size, bj+size + // the list of matching blocks is terminated by a + // sentinel with size 0 + if size > 0 { + opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) + } + } + m.opCodes = opCodes + return m.opCodes +} + +// GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes. +// +// Return a generator of groups with up to n lines of context. +// Each group is in the same format as returned by GetOpCodes(). +func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { + if n < 0 { + n = 3 + } + codes := m.GetOpCodes() + if len(codes) == 0 { + codes = []OpCode{{'e', 0, 1, 0, 1}} + } + // Fixup leading and trailing groups if they show no changes. + if codes[0].Tag == 'e' { + c := codes[0] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} + } + if codes[len(codes)-1].Tag == 'e' { + c := codes[len(codes)-1] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} + } + nn := n + n + groups := [][]OpCode{} + group := []OpCode{} + for _, c := range codes { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + // End the current group and start a new one whenever + // there is a large range with no changes. + if c.Tag == 'e' && i2-i1 > nn { + group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), + j1, min(j2, j1+n)}) + groups = append(groups, group) + group = []OpCode{} + i1, j1 = max(i1, i2-n), max(j1, j2-n) + } + group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) + } + if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { + groups = append(groups, group) + } + return groups +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go new file mode 100644 index 0000000..9897d4b --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff.go @@ -0,0 +1,161 @@ +package format + +import ( + "bytes" + "fmt" + "strings" + "unicode" + + "gotest.tools/v3/internal/difflib" +) + +const ( + contextLines = 2 +) + +// DiffConfig for a unified diff +type DiffConfig struct { + A string + B string + From string + To string +} + +// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better +// support for showing the whitespace differences. +func UnifiedDiff(conf DiffConfig) string { + a := strings.SplitAfter(conf.A, "\n") + b := strings.SplitAfter(conf.B, "\n") + groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines) + if len(groups) == 0 { + return "" + } + + buf := new(bytes.Buffer) + writeFormat := func(format string, args ...interface{}) { + buf.WriteString(fmt.Sprintf(format, args...)) + } + writeLine := func(prefix string, s string) { + buf.WriteString(prefix + s) + } + if hasWhitespaceDiffLines(groups, a, b) { + writeLine = visibleWhitespaceLine(writeLine) + } + formatHeader(writeFormat, conf) + for _, group := range groups { + formatRangeLine(writeFormat, group) + for _, opCode := range group { + in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2] + switch opCode.Tag { + case 'e': + formatLines(writeLine, " ", in) + case 'r': + formatLines(writeLine, "-", in) + formatLines(writeLine, "+", out) + case 'd': + formatLines(writeLine, "-", in) + case 'i': + formatLines(writeLine, "+", out) + } + } + } + return buf.String() +} + +// hasWhitespaceDiffLines returns true if any diff groups is only different +// because of whitespace characters. +func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool { + for _, group := range groups { + in, out := new(bytes.Buffer), new(bytes.Buffer) + for _, opCode := range group { + if opCode.Tag == 'e' { + continue + } + for _, line := range a[opCode.I1:opCode.I2] { + in.WriteString(line) + } + for _, line := range b[opCode.J1:opCode.J2] { + out.WriteString(line) + } + } + if removeWhitespace(in.String()) == removeWhitespace(out.String()) { + return true + } + } + return false +} + +func removeWhitespace(s string) string { + var result []rune + for _, r := range s { + if !unicode.IsSpace(r) { + result = append(result, r) + } + } + return string(result) +} + +func visibleWhitespaceLine(ws func(string, string)) func(string, string) { + mapToVisibleSpace := func(r rune) rune { + switch r { + case '\n': + case ' ': + return '·' + case '\t': + return '▷' + case '\v': + return '▽' + case '\r': + return '↵' + case '\f': + return '↓' + default: + if unicode.IsSpace(r) { + return '�' + } + } + return r + } + return func(prefix, s string) { + ws(prefix, strings.Map(mapToVisibleSpace, s)) + } +} + +func formatHeader(wf func(string, ...interface{}), conf DiffConfig) { + if conf.From != "" || conf.To != "" { + wf("--- %s\n", conf.From) + wf("+++ %s\n", conf.To) + } +} + +func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) { + first, last := group[0], group[len(group)-1] + range1 := formatRangeUnified(first.I1, last.I2) + range2 := formatRangeUnified(first.J1, last.J2) + wf("@@ -%s +%s @@\n", range1, range2) +} + +// Convert range to the "ed" format +func formatRangeUnified(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 1 { + return fmt.Sprintf("%d", beginning) + } + if length == 0 { + beginning-- // empty ranges begin at line just before the range + } + return fmt.Sprintf("%d,%d", beginning, length) +} + +func formatLines(writeLine func(string, string), prefix string, lines []string) { + for _, line := range lines { + writeLine(prefix, line) + } + // Add a newline if the last line is missing one so that the diff displays + // properly. + if !strings.HasSuffix(lines[len(lines)-1], "\n") { + writeLine("", "\n") + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go new file mode 100644 index 0000000..4986635 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/diff_test.go @@ -0,0 +1,71 @@ +package format_test + +import ( + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/golden" + "gotest.tools/v3/internal/format" +) + +func TestUnifiedDiff(t *testing.T) { + var testcases = []struct { + name string + a string + b string + expected string + from string + to string + }{ + { + name: "empty diff", + a: "a\nb\nc", + b: "a\nb\nc", + from: "from", + to: "to", + }, + { + name: "one diff with header", + a: "a\nxyz\nc", + b: "a\nb\nc", + from: "from", + to: "to", + expected: "one-diff-with-header.golden", + }, + { + name: "many diffs", + a: "a123\nxyz\nc\nbaba\nz\nt\nj2j2\nok\nok\ndone\n", + b: "a123\nxyz\nc\nabab\nz\nt\nj2j2\nok\nok\n", + expected: "many-diff.golden", + }, + { + name: "no trailing newline", + a: "a123\nxyz\nc\nbaba\nz\nt\nj2j2\nok\nok\ndone\n", + b: "a123\nxyz\nc\nabab\nz\nt\nj2j2\nok\nok", + expected: "many-diff-no-trailing-newline.golden", + }, + { + name: "whitespace diff", + a: " something\n something\n \v\r\n", + b: " something\n\tsomething\n \n", + expected: "whitespace-diff.golden", + }, + } + + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { + diff := format.UnifiedDiff(format.DiffConfig{ + A: testcase.a, + B: testcase.b, + From: testcase.from, + To: testcase.to, + }) + + if testcase.expected != "" { + assert.Assert(t, golden.String(diff, testcase.expected)) + return + } + assert.Equal(t, diff, "") + }) + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go new file mode 100644 index 0000000..5097e4b --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format.go @@ -0,0 +1,27 @@ +package format // import "gotest.tools/v3/internal/format" + +import "fmt" + +// Message accepts a msgAndArgs varargs and formats it using fmt.Sprintf +func Message(msgAndArgs ...interface{}) string { + switch len(msgAndArgs) { + case 0: + return "" + case 1: + return fmt.Sprintf("%v", msgAndArgs[0]) + default: + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } +} + +// WithCustomMessage accepts one or two messages and formats them appropriately +func WithCustomMessage(source string, msgAndArgs ...interface{}) string { + custom := Message(msgAndArgs...) + switch { + case custom == "": + return source + case source == "": + return custom + } + return fmt.Sprintf("%s: %s", source, custom) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go new file mode 100644 index 0000000..6404ad3 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/format_test.go @@ -0,0 +1,62 @@ +package format_test + +import ( + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/internal/format" +) + +func TestMessage(t *testing.T) { + var testcases = []struct { + doc string + args []interface{} + expected string + }{ + { + doc: "none", + }, + { + doc: "single string", + args: args("foo"), + expected: "foo", + }, + { + doc: "single non-string", + args: args(123), + expected: "123", + }, + { + doc: "format string and args", + args: args("%s %v", "a", 3), + expected: "a 3", + }, + } + + for _, tc := range testcases { + t.Run(tc.doc, func(t *testing.T) { + assert.Equal(t, format.Message(tc.args...), tc.expected) + }) + } +} + +func args(a ...interface{}) []interface{} { + return a +} + +func TestWithCustomMessage(t *testing.T) { + t.Run("only custom", func(t *testing.T) { + msg := format.WithCustomMessage("", "extra") + assert.Equal(t, msg, "extra") + }) + + t.Run("only source", func(t *testing.T) { + msg := format.WithCustomMessage("source") + assert.Equal(t, msg, "source") + }) + + t.Run("source and custom", func(t *testing.T) { + msg := format.WithCustomMessage("source", "extra") + assert.Equal(t, msg, "source: extra") + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden new file mode 100644 index 0000000..9518b0d --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff-no-trailing-newline.golden @@ -0,0 +1,13 @@ +@@ -2,10 +2,8 @@ + xyz + c +-baba ++abab + z + t + j2j2 + ok +-ok +-done +- ++ok diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden new file mode 100644 index 0000000..8202cbe --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/many-diff.golden @@ -0,0 +1,12 @@ +@@ -2,5 +2,5 @@ + xyz + c +-baba ++abab + z + t +@@ -8,4 +8,3 @@ + ok + ok +-done + diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden new file mode 100644 index 0000000..6012d3f --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/one-diff-with-header.golden @@ -0,0 +1,7 @@ +--- from ++++ to +@@ -1,3 +1,3 @@ + a +-xyz ++b + c diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden new file mode 100644 index 0000000..de9a187 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/format/testdata/whitespace-diff.golden @@ -0,0 +1,7 @@ +@@ -1,4 +1,4 @@ + ··something +-······something +-····▽↵ ++▷something ++·· + diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go new file mode 100644 index 0000000..6d6a7cf --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/maint/maint.go @@ -0,0 +1,28 @@ +package maint // import "gotest.tools/v3/internal/maint" + +import ( + "fmt" + "os" +) + +// T provides an implementation of assert.TestingT which uses os.Exit, and +// fmt.Println. This implementation can be used outside of test cases to provide +// assert.TestingT, for example in a TestMain. +var T = t{} + +type t struct{} + +// FailNow exits with a non-zero code +func (t t) FailNow() { + os.Exit(1) +} + +// Fail exits with a non-zero code +func (t t) Fail() { + os.Exit(2) +} + +// Log args by printing them to stdout +func (t t) Log(args ...interface{}) { + fmt.Println(args...) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go new file mode 100644 index 0000000..392d9fe --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/defers.go @@ -0,0 +1,52 @@ +package source + +import ( + "fmt" + "go/ast" + "go/token" +) + +func scanToDeferLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { + var matchedNode ast.Node + ast.Inspect(node, func(node ast.Node) bool { + switch { + case node == nil || matchedNode != nil: + return false + case fileset.Position(node.End()).Line == lineNum: + if funcLit, ok := node.(*ast.FuncLit); ok { + matchedNode = funcLit + return false + } + } + return true + }) + debug("defer line node: %s", debugFormatNode{matchedNode}) + return matchedNode +} + +func guessDefer(node ast.Node) (ast.Node, error) { + defers := collectDefers(node) + switch len(defers) { + case 0: + return nil, fmt.Errorf("failed to find expression in defer") + case 1: + return defers[0].Call, nil + default: + return nil, fmt.Errorf( + "ambiguous call expression: multiple (%d) defers in call block", + len(defers)) + } +} + +func collectDefers(node ast.Node) []*ast.DeferStmt { + var defers []*ast.DeferStmt + ast.Inspect(node, func(node ast.Node) bool { + if d, ok := node.(*ast.DeferStmt); ok { + defers = append(defers, d) + debug("defer: %s", debugFormatNode{d}) + return false + } + return true + }) + return defers +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go new file mode 100644 index 0000000..a3f7008 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source.go @@ -0,0 +1,147 @@ +package source // import "gotest.tools/v3/internal/source" + +import ( + "bytes" + "errors" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" + "runtime" +) + +// FormattedCallExprArg returns the argument from an ast.CallExpr at the +// index in the call stack. The argument is formatted using FormatNode. +func FormattedCallExprArg(stackIndex int, argPos int) (string, error) { + args, err := CallExprArgs(stackIndex + 1) + if err != nil { + return "", err + } + if argPos >= len(args) { + return "", errors.New("failed to find expression") + } + return FormatNode(args[argPos]) +} + +// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at +// the index in the call stack. +func CallExprArgs(stackIndex int) ([]ast.Expr, error) { + _, filename, line, ok := runtime.Caller(stackIndex + 1) + if !ok { + return nil, errors.New("failed to get call stack") + } + debug("call stack position: %s:%d", filename, line) + + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) + if err != nil { + return nil, fmt.Errorf("failed to parse source file %s: %w", filename, err) + } + + expr, err := getCallExprArgs(fileset, astFile, line) + if err != nil { + return nil, fmt.Errorf("call from %s:%d: %w", filename, line, err) + } + return expr, nil +} + +func getNodeAtLine(fileset *token.FileSet, astFile ast.Node, lineNum int) (ast.Node, error) { + if node := scanToLine(fileset, astFile, lineNum); node != nil { + return node, nil + } + if node := scanToDeferLine(fileset, astFile, lineNum); node != nil { + node, err := guessDefer(node) + if err != nil || node != nil { + return node, err + } + } + return nil, nil +} + +func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { + var matchedNode ast.Node + ast.Inspect(node, func(node ast.Node) bool { + switch { + case node == nil || matchedNode != nil: + return false + case fileset.Position(node.Pos()).Line == lineNum: + matchedNode = node + return false + } + return true + }) + return matchedNode +} + +func getCallExprArgs(fileset *token.FileSet, astFile ast.Node, line int) ([]ast.Expr, error) { + node, err := getNodeAtLine(fileset, astFile, line) + switch { + case err != nil: + return nil, err + case node == nil: + return nil, fmt.Errorf("failed to find an expression") + } + + debug("found node: %s", debugFormatNode{node}) + + visitor := &callExprVisitor{} + ast.Walk(visitor, node) + if visitor.expr == nil { + return nil, errors.New("failed to find call expression") + } + debug("callExpr: %s", debugFormatNode{visitor.expr}) + return visitor.expr.Args, nil +} + +type callExprVisitor struct { + expr *ast.CallExpr +} + +func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor { + if v.expr != nil || node == nil { + return nil + } + debug("visit: %s", debugFormatNode{node}) + + switch typed := node.(type) { + case *ast.CallExpr: + v.expr = typed + return nil + case *ast.DeferStmt: + ast.Walk(v, typed.Call.Fun) + return nil + } + return v +} + +// FormatNode using go/format.Node and return the result as a string +func FormatNode(node ast.Node) (string, error) { + buf := new(bytes.Buffer) + err := format.Node(buf, token.NewFileSet(), node) + return buf.String(), err +} + +var debugEnabled = os.Getenv("GOTESTTOOLS_DEBUG") != "" + +func debug(format string, args ...interface{}) { + if debugEnabled { + fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...) + } +} + +type debugFormatNode struct { + ast.Node +} + +func (n debugFormatNode) String() string { + if n.Node == nil { + return "none" + } + out, err := FormatNode(n.Node) + if err != nil { + return fmt.Sprintf("failed to format %s: %s", n.Node, err) + } + return fmt.Sprintf("(%T) %s", n.Node, out) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go new file mode 100644 index 0000000..3c21819 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/source_test.go @@ -0,0 +1,94 @@ +package source_test + +// using a separate package for test to avoid circular imports with the assert +// package + +import ( + "fmt" + "runtime" + "strings" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/internal/source" + "gotest.tools/v3/skip" +) + +func TestFormattedCallExprArg_SingleLine(t *testing.T) { + msg, err := shim("not", "this", "this text") + assert.NilError(t, err) + assert.Equal(t, `"this text"`, msg) +} + +func TestFormattedCallExprArg_MultiLine(t *testing.T) { + msg, err := shim( + "first", + "second", + "this text", + ) + assert.NilError(t, err) + assert.Equal(t, `"this text"`, msg) +} + +func TestFormattedCallExprArg_IfStatement(t *testing.T) { + if msg, err := shim( + "first", + "second", + "this text", + ); true { + assert.NilError(t, err) + assert.Equal(t, `"this text"`, msg) + } +} + +func shim(_, _, _ string) (string, error) { + return source.FormattedCallExprArg(1, 2) +} + +func TestFormattedCallExprArg_InDefer(t *testing.T) { + skip.If(t, isGoVersion18) + cap := &capture{} + func() { + defer cap.shim("first", "second") + }() + + assert.NilError(t, cap.err) + assert.Equal(t, cap.value, `"second"`) +} + +func isGoVersion18() bool { + return strings.HasPrefix(runtime.Version(), "go1.8.") +} + +type capture struct { + value string + err error +} + +func (c *capture) shim(_, _ string) { + c.value, c.err = source.FormattedCallExprArg(1, 1) +} + +func TestFormattedCallExprArg_InAnonymousDefer(t *testing.T) { + cap := &capture{} + func() { + fmt.Println() + defer fmt.Println() + defer func() { cap.shim("first", "second") }() + }() + + assert.NilError(t, cap.err) + assert.Equal(t, cap.value, `"second"`) +} + +func TestFormattedCallExprArg_InDeferMultipleDefers(t *testing.T) { + skip.If(t, isGoVersion18) + cap := &capture{} + func() { + fmt.Println() + defer fmt.Println() + defer cap.shim("first", "second") + }() + + assert.ErrorContains(t, cap.err, "ambiguous call expression") +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/update.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/update.go new file mode 100644 index 0000000..bd9678b --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/update.go @@ -0,0 +1,138 @@ +package source + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" + "runtime" + "strings" +) + +// Update is set by the -update flag. It indicates the user running the tests +// would like to update any golden values. +var Update bool + +func init() { + flag.BoolVar(&Update, "update", false, "update golden values") +} + +// ErrNotFound indicates that UpdateExpectedValue failed to find the +// variable to update, likely because it is not a package level variable. +var ErrNotFound = fmt.Errorf("failed to find variable for update of golden value") + +// UpdateExpectedValue looks for a package-level variable with a name that +// starts with expected in the arguments to the caller. If the variable is +// found, the value of the variable will be updated to value of the other +// argument to the caller. +func UpdateExpectedValue(stackIndex int, x, y interface{}) error { + _, filename, line, ok := runtime.Caller(stackIndex + 1) + if !ok { + return errors.New("failed to get call stack") + } + debug("call stack position: %s:%d", filename, line) + + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors|parser.ParseComments) + if err != nil { + return fmt.Errorf("failed to parse source file %s: %w", filename, err) + } + + expr, err := getCallExprArgs(fileset, astFile, line) + if err != nil { + return fmt.Errorf("call from %s:%d: %w", filename, line, err) + } + + if len(expr) < 3 { + debug("not enough arguments %d: %v", + len(expr), debugFormatNode{Node: &ast.CallExpr{Args: expr}}) + return ErrNotFound + } + + argIndex, varName := getVarNameForExpectedValueArg(expr) + if argIndex < 0 || varName == "" { + debug("no arguments started with the word 'expected': %v", + debugFormatNode{Node: &ast.CallExpr{Args: expr}}) + return ErrNotFound + } + + value := x + if argIndex == 1 { + value = y + } + + strValue, ok := value.(string) + if !ok { + debug("value must be type string, got %T", value) + return ErrNotFound + } + return UpdateVariable(filename, fileset, astFile, varName, strValue) +} + +// UpdateVariable writes to filename the contents of astFile with the value of +// the variable updated to value. +func UpdateVariable( + filename string, + fileset *token.FileSet, + astFile *ast.File, + varName string, + value string, +) error { + obj := astFile.Scope.Objects[varName] + if obj == nil { + return ErrNotFound + } + if obj.Kind != ast.Con && obj.Kind != ast.Var { + debug("can only update var and const, found %v", obj.Kind) + return ErrNotFound + } + + spec, ok := obj.Decl.(*ast.ValueSpec) + if !ok { + debug("can only update *ast.ValueSpec, found %T", obj.Decl) + return ErrNotFound + } + if len(spec.Names) != 1 { + debug("more than one name in ast.ValueSpec") + return ErrNotFound + } + + spec.Values[0] = &ast.BasicLit{ + Kind: token.STRING, + Value: "`" + value + "`", + } + + var buf bytes.Buffer + if err := format.Node(&buf, fileset, astFile); err != nil { + return fmt.Errorf("failed to format file after update: %w", err) + } + + fh, err := os.Create(filename) + if err != nil { + return fmt.Errorf("failed to open file %v: %w", filename, err) + } + if _, err = fh.Write(buf.Bytes()); err != nil { + return fmt.Errorf("failed to write file %v: %w", filename, err) + } + if err := fh.Sync(); err != nil { + return fmt.Errorf("failed to sync file %v: %w", filename, err) + } + return nil +} + +func getVarNameForExpectedValueArg(expr []ast.Expr) (int, string) { + for i := 1; i < 3; i++ { + switch e := expr[i].(type) { + case *ast.Ident: + if strings.HasPrefix(strings.ToLower(e.Name), "expected") { + return i, e.Name + } + } + } + return -1, "" +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/version.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/version.go new file mode 100644 index 0000000..5fa8a90 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/internal/source/version.go @@ -0,0 +1,35 @@ +package source + +import ( + "runtime" + "strconv" + "strings" +) + +// GoVersionLessThan returns true if runtime.Version() is semantically less than +// version major.minor. Returns false if a release version can not be parsed from +// runtime.Version(). +func GoVersionLessThan(major, minor int64) bool { + version := runtime.Version() + // not a release version + if !strings.HasPrefix(version, "go") { + return false + } + version = strings.TrimPrefix(version, "go") + parts := strings.Split(version, ".") + if len(parts) < 2 { + return false + } + rMajor, err := strconv.ParseInt(parts[0], 10, 32) + if err != nil { + return false + } + if rMajor != major { + return rMajor < major + } + rMinor, err := strconv.ParseInt(parts[1], 10, 32) + if err != nil { + return false + } + return rMinor < minor +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go new file mode 100644 index 0000000..e7a858a --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/pkg.go @@ -0,0 +1,4 @@ +/*Package gotesttools is a collection of packages to augment `testing` and +support common patterns. +*/ +package gotesttools // import "gotest.tools/v3" diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go new file mode 100644 index 0000000..46880f5 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check.go @@ -0,0 +1,47 @@ +package poll + +import ( + "net" + "os" +) + +// Check is a function which will be used as check for the WaitOn method. +type Check func(t LogT) Result + +// FileExists looks on filesystem and check that path exists. +func FileExists(path string) Check { + return func(t LogT) Result { + if h, ok := t.(helperT); ok { + h.Helper() + } + + _, err := os.Stat(path) + switch { + case os.IsNotExist(err): + t.Logf("waiting on file %s to exist", path) + return Continue("file %s does not exist", path) + case err != nil: + return Error(err) + default: + return Success() + } + } +} + +// Connection try to open a connection to the address on the +// named network. See net.Dial for a description of the network and +// address parameters. +func Connection(network, address string) Check { + return func(t LogT) Result { + if h, ok := t.(helperT); ok { + h.Helper() + } + + _, err := net.Dial(network, address) + if err != nil { + t.Logf("waiting on socket %s://%s to be available...", network, address) + return Continue("socket %s://%s not available", network, address) + } + return Success() + } +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go new file mode 100644 index 0000000..c853838 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/check_test.go @@ -0,0 +1,42 @@ +package poll + +import ( + "fmt" + "os" + "testing" + + "gotest.tools/v3/assert" +) + +func TestWaitOnFile(t *testing.T) { + fakeFilePath := "./fakefile" + + check := FileExists(fakeFilePath) + + t.Run("file does not exist", func(t *testing.T) { + r := check(t) + assert.Assert(t, !r.Done()) + assert.Equal(t, r.Message(), fmt.Sprintf("file %s does not exist", fakeFilePath)) + }) + + os.Create(fakeFilePath) + defer os.Remove(fakeFilePath) + + t.Run("file exists", func(t *testing.T) { + assert.Assert(t, check(t).Done()) + }) +} + +func TestWaitOnSocketWithTimeout(t *testing.T) { + t.Run("connection to unavailable address", func(t *testing.T) { + check := Connection("tcp", "foo.bar:55555") + r := check(t) + assert.Assert(t, !r.Done()) + assert.Equal(t, r.Message(), "socket tcp://foo.bar:55555 not available") + }) + + t.Run("connection to ", func(t *testing.T) { + check := Connection("tcp", "google.com:80") + assert.Assert(t, check(t).Done()) + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go new file mode 100644 index 0000000..2e3c32d --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/example_test.go @@ -0,0 +1,45 @@ +package poll_test + +import ( + "fmt" + "time" + + "gotest.tools/v3/poll" +) + +var t poll.TestingT + +func numOfProcesses() (int, error) { + return 0, nil +} + +func ExampleWaitOn() { + desired := 10 + + check := func(t poll.LogT) poll.Result { + actual, err := numOfProcesses() + if err != nil { + return poll.Error(fmt.Errorf("failed to get number of processes: %w", err)) + } + if actual == desired { + return poll.Success() + } + t.Logf("waiting on process count to be %d...", desired) + return poll.Continue("number of processes is %d, not %d", actual, desired) + } + + poll.WaitOn(t, check) +} + +func isDesiredState() bool { return false } +func getState() string { return "" } + +func ExampleSettingOp() { + check := func(t poll.LogT) poll.Result { + if isDesiredState() { + return poll.Success() + } + return poll.Continue("state is: %s", getState()) + } + poll.WaitOn(t, check, poll.WithTimeout(30*time.Second), poll.WithDelay(15*time.Millisecond)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go new file mode 100644 index 0000000..29c5b40 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll.go @@ -0,0 +1,171 @@ +/*Package poll provides tools for testing asynchronous code. + */ +package poll // import "gotest.tools/v3/poll" + +import ( + "fmt" + "strings" + "time" + + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/assert" +) + +// TestingT is the subset of testing.T used by WaitOn +type TestingT interface { + LogT + Fatalf(format string, args ...interface{}) +} + +// LogT is a logging interface that is passed to the WaitOn check function +type LogT interface { + Log(args ...interface{}) + Logf(format string, args ...interface{}) +} + +type helperT interface { + Helper() +} + +// Settings are used to configure the behaviour of WaitOn +type Settings struct { + // Timeout is the maximum time to wait for the condition. Defaults to 10s. + Timeout time.Duration + // Delay is the time to sleep between checking the condition. Defaults to + // 100ms. + Delay time.Duration +} + +func defaultConfig() *Settings { + return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond} +} + +// SettingOp is a function which accepts and modifies Settings +type SettingOp func(config *Settings) + +// WithDelay sets the delay to wait between polls +func WithDelay(delay time.Duration) SettingOp { + return func(config *Settings) { + config.Delay = delay + } +} + +// WithTimeout sets the timeout +func WithTimeout(timeout time.Duration) SettingOp { + return func(config *Settings) { + config.Timeout = timeout + } +} + +// Result of a check performed by WaitOn +type Result interface { + // Error indicates that the check failed and polling should stop, and the + // the has failed + Error() error + // Done indicates that polling should stop, and the test should proceed + Done() bool + // Message provides the most recent state when polling has not completed + Message() string +} + +type result struct { + done bool + message string + err error +} + +func (r result) Done() bool { + return r.done +} + +func (r result) Message() string { + return r.message +} + +func (r result) Error() error { + return r.err +} + +// Continue returns a Result that indicates to WaitOn that it should continue +// polling. The message text will be used as the failure message if the timeout +// is reached. +func Continue(message string, args ...interface{}) Result { + return result{message: fmt.Sprintf(message, args...)} +} + +// Success returns a Result where Done() returns true, which indicates to WaitOn +// that it should stop polling and exit without an error. +func Success() Result { + return result{done: true} +} + +// Error returns a Result that indicates to WaitOn that it should fail the test +// and stop polling. +func Error(err error) Result { + return result{err: err} +} + +// WaitOn a condition or until a timeout. Poll by calling check and exit when +// check returns a done Result. To fail a test and exit polling with an error +// return a error result. +func WaitOn(t TestingT, check Check, pollOps ...SettingOp) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + config := defaultConfig() + for _, pollOp := range pollOps { + pollOp(config) + } + + var lastMessage string + after := time.After(config.Timeout) + chResult := make(chan Result) + for { + go func() { + chResult <- check(t) + }() + select { + case <-after: + if lastMessage == "" { + lastMessage = "first check never completed" + } + t.Fatalf("timeout hit after %s: %s", config.Timeout, lastMessage) + case result := <-chResult: + switch { + case result.Error() != nil: + t.Fatalf("polling check failed: %s", result.Error()) + case result.Done(): + return + } + time.Sleep(config.Delay) + lastMessage = result.Message() + } + } +} + +// Compare values using the cmp.Comparison. If the comparison fails return a +// result which indicates to WaitOn that it should continue waiting. +// If the comparison is successful then WaitOn stops polling. +func Compare(compare cmp.Comparison) Result { + buf := new(logBuffer) + if assert.RunComparison(buf, assert.ArgsAtZeroIndex, compare) { + return Success() + } + return Continue(buf.String()) +} + +type logBuffer struct { + log [][]interface{} +} + +func (c *logBuffer) Log(args ...interface{}) { + c.log = append(c.log, args) +} + +func (c *logBuffer) String() string { + b := new(strings.Builder) + for _, item := range c.log { + b.WriteString(fmt.Sprint(item...) + " ") + } + return b.String() +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go new file mode 100644 index 0000000..36bd457 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/poll/poll_test.go @@ -0,0 +1,87 @@ +package poll + +import ( + "fmt" + "testing" + "time" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +type fakeT struct { + failed string +} + +func (t *fakeT) Fatalf(format string, args ...interface{}) { + t.failed = fmt.Sprintf(format, args...) + panic("exit wait on") +} + +func (t *fakeT) Log(args ...interface{}) {} + +func (t *fakeT) Logf(format string, args ...interface{}) {} + +func TestWaitOn(t *testing.T) { + counter := 0 + end := 4 + check := func(t LogT) Result { + if counter == end { + return Success() + } + counter++ + return Continue("counter is at %d not yet %d", counter-1, end) + } + + WaitOn(t, check, WithDelay(0)) + assert.Equal(t, end, counter) +} + +func TestWaitOnWithTimeout(t *testing.T) { + fakeT := &fakeT{} + + check := func(t LogT) Result { + return Continue("not done") + } + + assert.Assert(t, cmp.Panics(func() { + WaitOn(fakeT, check, WithTimeout(time.Millisecond)) + })) + assert.Equal(t, "timeout hit after 1ms: not done", fakeT.failed) +} + +func TestWaitOnWithCheckTimeout(t *testing.T) { + fakeT := &fakeT{} + + check := func(t LogT) Result { + time.Sleep(1 * time.Second) + return Continue("not done") + } + + assert.Assert(t, cmp.Panics(func() { WaitOn(fakeT, check, WithTimeout(time.Millisecond)) })) + assert.Equal(t, "timeout hit after 1ms: first check never completed", fakeT.failed) +} + +func TestWaitOnWithCheckError(t *testing.T) { + fakeT := &fakeT{} + + check := func(t LogT) Result { + return Error(fmt.Errorf("broke")) + } + + assert.Assert(t, cmp.Panics(func() { WaitOn(fakeT, check) })) + assert.Equal(t, "polling check failed: broke", fakeT.failed) +} + +func TestWaitOn_WithCompare(t *testing.T) { + fakeT := &fakeT{} + + check := func(t LogT) Result { + return Compare(cmp.Equal(3, 4)) + } + + assert.Assert(t, cmp.Panics(func() { + WaitOn(fakeT, check, WithDelay(0), WithTimeout(10*time.Millisecond)) + })) + assert.Assert(t, cmp.Contains(fakeT.failed, "assertion failed: 3 (int) != 4 (int)")) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify new file mode 100644 index 0000000..cb79fdd --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/binary-gty-migrate-from-testify @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +exec go build -o ./dist/gty-migrate-from-testify ./assert/cmd/gty-migrate-from-testify diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit new file mode 100644 index 0000000..0ed48f0 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/scripts/test-unit @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +paths=${@:-$(go list ./... | grep -v '/vendor/')} +TESTFLAGS=${TESTFLAGS-} +TESTTIMEOUT=${TESTTIMEOUT-30s} +go test -test.timeout "$TESTTIMEOUT" $TESTFLAGS -v $paths diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go new file mode 100644 index 0000000..31c2148 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/example_test.go @@ -0,0 +1,43 @@ +package skip_test + +import ( + "testing" + + "gotest.tools/v3/skip" +) + +var apiVersion = "" + +type env struct{} + +func (e env) hasFeature(_ string) bool { return false } + +var testEnv = env{} + +func MissingFeature() bool { return false } + +var t = &testing.T{} + +func ExampleIf() { + // --- SKIP: TestName (0.00s) + // skip.go:19: MissingFeature + skip.If(t, MissingFeature) + + // --- SKIP: TestName (0.00s) + // skip.go:19: MissingFeature: coming soon + skip.If(t, MissingFeature, "coming soon") +} + +func ExampleIf_withExpression() { + // --- SKIP: TestName (0.00s) + // skip.go:19: apiVersion < version("v1.24") + skip.If(t, apiVersion < version("v1.24")) + + // --- SKIP: TestName (0.00s) + // skip.go:19: !textenv.hasFeature("build"): coming soon + skip.If(t, !testEnv.hasFeature("build"), "coming soon") +} + +func version(v string) string { + return v +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go new file mode 100644 index 0000000..cb899f7 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip.go @@ -0,0 +1,89 @@ +/*Package skip provides functions for skipping a test and printing the source code +of the condition used to skip the test. +*/ +package skip // import "gotest.tools/v3/skip" + +import ( + "fmt" + "path" + "reflect" + "runtime" + "strings" + + "gotest.tools/v3/internal/format" + "gotest.tools/v3/internal/source" +) + +type skipT interface { + Skip(args ...interface{}) + Log(args ...interface{}) +} + +// Result of skip function +type Result interface { + Skip() bool + Message() string +} + +type helperT interface { + Helper() +} + +// BoolOrCheckFunc can be a bool, func() bool, or func() Result. Other types will panic +type BoolOrCheckFunc interface{} + +// If the condition expression evaluates to true, skip the test. +// +// The condition argument may be one of three types: bool, func() bool, or +// func() SkipResult. +// When called with a bool, the test will be skip if the condition evaluates to true. +// When called with a func() bool, the test will be skip if the function returns true. +// When called with a func() Result, the test will be skip if the Skip method +// of the result returns true. +// The skip message will contain the source code of the expression. +// Extra message text can be passed as a format string with args. +func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + switch check := condition.(type) { + case bool: + ifCondition(t, check, msgAndArgs...) + case func() bool: + if check() { + t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...)) + } + case func() Result: + result := check() + if result.Skip() { + msg := getFunctionName(check) + ": " + result.Message() + t.Skip(format.WithCustomMessage(msg, msgAndArgs...)) + } + default: + panic(fmt.Sprintf("invalid type for condition arg: %T", check)) + } +} + +func getFunctionName(function interface{}) string { + funcPath := runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name() + return strings.SplitN(path.Base(funcPath), ".", 2)[1] +} + +func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !condition { + return + } + const ( + stackIndex = 2 + argPos = 1 + ) + source, err := source.FormattedCallExprArg(stackIndex, argPos) + if err != nil { + t.Log(err.Error()) + t.Skip(format.Message(msgAndArgs...)) + } + t.Skip(format.WithCustomMessage(source, msgAndArgs...)) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go new file mode 100644 index 0000000..49b4dda --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/skip/skip_test.go @@ -0,0 +1,136 @@ +package skip + +import ( + "bytes" + "fmt" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/assert/cmp" +) + +type fakeSkipT struct { + reason string + logs []string +} + +func (f *fakeSkipT) Skip(args ...interface{}) { + buf := new(bytes.Buffer) + for _, arg := range args { + buf.WriteString(fmt.Sprintf("%s", arg)) + } + f.reason = buf.String() +} + +func (f *fakeSkipT) Log(args ...interface{}) { + f.logs = append(f.logs, fmt.Sprintf("%s", args[0])) +} + +func (f *fakeSkipT) Helper() {} + +func version(v string) string { + return v +} + +func TestIfCondition(t *testing.T) { + skipT := &fakeSkipT{} + apiVersion := "v1.4" + If(skipT, apiVersion < version("v1.6")) + + assert.Equal(t, `apiVersion < version("v1.6")`, skipT.reason) + assert.Assert(t, cmp.Len(skipT.logs, 0)) +} + +func TestIfConditionWithMessage(t *testing.T) { + skipT := &fakeSkipT{} + apiVersion := "v1.4" + If(skipT, apiVersion < "v1.6", "see notes") + + assert.Equal(t, `apiVersion < "v1.6": see notes`, skipT.reason) + assert.Assert(t, cmp.Len(skipT.logs, 0)) +} + +func TestIfConditionMultiline(t *testing.T) { + skipT := &fakeSkipT{} + apiVersion := "v1.4" + If( + skipT, + apiVersion < "v1.6") + + assert.Equal(t, `apiVersion < "v1.6"`, skipT.reason) + assert.Assert(t, cmp.Len(skipT.logs, 0)) +} + +func TestIfConditionMultilineWithMessage(t *testing.T) { + skipT := &fakeSkipT{} + apiVersion := "v1.4" + If( + skipT, + apiVersion < "v1.6", + "see notes") + + assert.Equal(t, `apiVersion < "v1.6": see notes`, skipT.reason) + assert.Assert(t, cmp.Len(skipT.logs, 0)) +} + +func TestIfConditionNoSkip(t *testing.T) { + skipT := &fakeSkipT{} + If(skipT, false) + + assert.Equal(t, "", skipT.reason) + assert.Assert(t, cmp.Len(skipT.logs, 0)) +} + +func SkipBecauseISaidSo() bool { + return true +} + +func TestIf(t *testing.T) { + skipT := &fakeSkipT{} + If(skipT, SkipBecauseISaidSo) + + assert.Equal(t, "SkipBecauseISaidSo", skipT.reason) +} + +func TestIfWithMessage(t *testing.T) { + skipT := &fakeSkipT{} + If(skipT, SkipBecauseISaidSo, "see notes") + + assert.Equal(t, "SkipBecauseISaidSo: see notes", skipT.reason) +} + +func TestIf_InvalidCondition(t *testing.T) { + skipT := &fakeSkipT{} + assert.Assert(t, cmp.Panics(func() { + If(skipT, "just a string") + })) +} + +func TestIfWithSkipResultFunc(t *testing.T) { + t.Run("no extra message", func(t *testing.T) { + skipT := &fakeSkipT{} + If(skipT, alwaysSkipWithMessage) + + assert.Equal(t, "alwaysSkipWithMessage: skip because I said so!", skipT.reason) + }) + t.Run("with extra message", func(t *testing.T) { + skipT := &fakeSkipT{} + If(skipT, alwaysSkipWithMessage, "also %v", 4) + + assert.Equal(t, "alwaysSkipWithMessage: skip because I said so!: also 4", skipT.reason) + }) +} + +func alwaysSkipWithMessage() Result { + return skipResult{} +} + +type skipResult struct{} + +func (s skipResult) Skip() bool { + return true +} + +func (s skipResult) Message() string { + return "skip because I said so!" +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go new file mode 100644 index 0000000..90f4541 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/doc.go @@ -0,0 +1,5 @@ +/*Package x is a namespace for experimental packages. Packages under x have looser +compatibility requirements. Packages in this namespace may contain backwards +incompatible changes within the same major version. +*/ +package x // import "gotest.tools/v3/x" diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go new file mode 100644 index 0000000..708d940 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context.go @@ -0,0 +1,93 @@ +/*Package subtest provides a TestContext to subtests which handles cleanup, and +provides a testing.TB, and context.Context. + +This package was inspired by github.com/frankban/quicktest. + +DEPRECATED + +With the addition of T.Cleanup() in go1.14 this package provides very +little value. A context.Context can be managed by tests that need it with +little enough boilerplate that it doesn't make sense to wrap testing.T in a +TestContext. +*/ +package subtest // import "gotest.tools/v3/x/subtest" + +import ( + "context" + "testing" + + "gotest.tools/v3/internal/cleanup" +) + +type testcase struct { + testing.TB + ctx context.Context + cleanupFuncs []cleanupFunc +} + +type cleanupFunc func() + +func (tc *testcase) Ctx() context.Context { + if tc.ctx == nil { + var cancel func() + tc.ctx, cancel = context.WithCancel(context.Background()) + cleanup.Cleanup(tc, cancel) + } + return tc.ctx +} + +// cleanup runs all cleanup functions. Functions are run in the opposite order +// in which they were added. Cleanup is called automatically before Run exits. +func (tc *testcase) cleanup() { + for _, f := range tc.cleanupFuncs { + // Defer all cleanup functions so they all run even if one calls + // t.FailNow() or panics. Deferring them also runs them in reverse order. + defer f() + } + tc.cleanupFuncs = nil +} + +func (tc *testcase) AddCleanup(f func()) { + tc.cleanupFuncs = append(tc.cleanupFuncs, f) +} + +func (tc *testcase) Parallel() { + tp, ok := tc.TB.(parallel) + if !ok { + panic("Parallel called with a testing.B") + } + tp.Parallel() +} + +type parallel interface { + Parallel() +} + +// Run a subtest. When subtest exits, every cleanup function added with +// TestContext.AddCleanup will be run. +func Run(t *testing.T, name string, subtest func(t TestContext)) bool { + return t.Run(name, func(t *testing.T) { + tc := &testcase{TB: t} + defer tc.cleanup() + subtest(tc) + }) +} + +// TestContext provides a testing.TB and a context.Context for a test case. +type TestContext interface { + testing.TB + // AddCleanup function which will be run when before Run returns. + // + // Deprecated: Go 1.14+ now includes a testing.TB.Cleanup(func()) which + // should be used instead. AddCleanup will be removed in a future release. + AddCleanup(f func()) + // Ctx returns a context for the test case. Multiple calls from the same subtest + // will return the same context. The context is cancelled when Run + // returns. + Ctx() context.Context + // Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does + // not implement Parallel. + Parallel() +} + +var _ TestContext = &testcase{} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go new file mode 100644 index 0000000..56b316e --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/context_test.go @@ -0,0 +1,32 @@ +package subtest + +import ( + "context" + "testing" + + "gotest.tools/v3/assert" +) + +func TestTestcase_Run_CallsCleanup(t *testing.T) { + calls := []int{} + var ctx context.Context + Run(t, "test-run-cleanup", func(t TestContext) { + cleanup := func(n int) func() { + return func() { + calls = append(calls, n) + } + } + ctx = t.Ctx() + t.AddCleanup(cleanup(2)) + t.AddCleanup(cleanup(1)) + t.AddCleanup(cleanup(0)) + }) + assert.DeepEqual(t, calls, []int{0, 1, 2}) + assert.Equal(t, ctx.Err(), context.Canceled) +} + +func TestTestcase_Run_Parallel(t *testing.T) { + Run(t, "test-parallel", func(t TestContext) { + t.Parallel() + }) +} diff --git a/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go new file mode 100644 index 0000000..b4365e0 --- /dev/null +++ b/root/pkg/mod/gotest.tools/v3@v3.3.0/x/subtest/example_test.go @@ -0,0 +1,66 @@ +package subtest_test + +import ( + "io" + "net/http" + "strings" + "testing" + + "gotest.tools/v3/assert" + "gotest.tools/v3/x/subtest" +) + +var t = &testing.T{} + +func ExampleRun_tableTest() { + var testcases = []struct { + data io.Reader + expected int + }{ + { + data: strings.NewReader("invalid input"), + expected: 400, + }, + { + data: strings.NewReader("valid input"), + expected: 200, + }, + } + + for _, tc := range testcases { + subtest.Run(t, "test-service-call", func(t subtest.TestContext) { + // startFakeService can shutdown using t.AddCleanup + url := startFakeService(t) + + req, err := http.NewRequest("POST", url, tc.data) + assert.NilError(t, err) + req = req.WithContext(t.Ctx()) + + client := newClient(t) + resp, err := client.Do(req) + assert.NilError(t, err) + assert.Equal(t, resp.StatusCode, tc.expected) + }) + } +} + +func startFakeService(t subtest.TestContext) string { + return "url" +} + +func newClient(_ subtest.TestContext) *http.Client { + return &http.Client{} +} + +func ExampleRun_testSuite() { + // do suite setup before subtests + + subtest.Run(t, "test-one", func(t subtest.TestContext) { + assert.Equal(t, 1, 1) + }) + subtest.Run(t, "test-two", func(t subtest.TestContext) { + assert.Equal(t, 2, 2) + }) + + // do suite teardown after subtests +} diff --git a/root/pkg/sumdb/sum.golang.org/latest b/root/pkg/sumdb/sum.golang.org/latest new file mode 100644 index 0000000..829c8df --- /dev/null +++ b/root/pkg/sumdb/sum.golang.org/latest @@ -0,0 +1,5 @@ +go.sum database tree +46687924 +oqyj00KFhQ1r/LMLYjS+N2ZcyMBmuy4E20Qx5wubH18= + +— sum.golang.org Az3grskfHYkRAlx6xDdD0Ld7PWz6CLORxgtPBxvd73HO8lvyC+Z2yYTTobhK5XRat+vJH6yqO8Oqxa4++MKlzqCl/QY= diff --git a/root/src/github.com/getyoti/age-scan-examples/go.sum b/root/src/github.com/getyoti/age-scan-examples/go.sum new file mode 100644 index 0000000..e553463 --- /dev/null +++ b/root/src/github.com/getyoti/age-scan-examples/go.sum @@ -0,0 +1,8 @@ +github.com/getyoti/yoti-go-sdk/v3 v3.14.0 h1:cMFC/PuN6kuxMfPwX4OkVyQDA5ZousWnVMfUhIhKZa0= +github.com/getyoti/yoti-go-sdk/v3 v3.14.0/go.mod h1:FH8g7mRttc6SBUd9P0Jihm7ut0rNhkU3rDFljUHL33I= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= From 434ad820c6c27012b602831021c17fbbc2b1a5dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 17:57:53 +0000 Subject: [PATCH 08/18] Add .gitignore to exclude build artifacts and Go cache Co-authored-by: saurabh-yoti <108520161+saurabh-yoti@users.noreply.github.com> --- .gitignore | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8861a03 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +# Build artifacts and dependencies +node_modules/ +vendor/ +target/ +dist/ +build/ +bin/ +obj/ + +# Go module cache +/root/ + +# IDE files +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Environment and keys +.env +*.pem +!.env.example + +# Logs +*.log From 4997bf090a8bf3e61ffa3b2d46c7330e14269291 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 18:00:30 +0000 Subject: [PATCH 09/18] Remove .NET build artifacts that should not be committed --- .../obj/CoreExample.csproj.nuget.dgspec.json | 111 - .../obj/CoreExample.csproj.nuget.g.props | 15 - .../obj/CoreExample.csproj.nuget.g.targets | 2 - .../CoreExample/obj/project.assets.json | 5889 ----------------- .../CoreExample/obj/project.nuget.cache | 124 - 5 files changed, 6141 deletions(-) delete mode 100644 dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json delete mode 100644 dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props delete mode 100644 dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets delete mode 100644 dotnet/CoreExample/CoreExample/obj/project.assets.json delete mode 100644 dotnet/CoreExample/CoreExample/obj/project.nuget.cache diff --git a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json deleted file mode 100644 index 4479729..0000000 --- a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.dgspec.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "format": 1, - "restore": { - "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj": {} - }, - "projects": { - "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", - "projectName": "CoreExample", - "projectPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", - "packagesPath": "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages", - "outputPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/obj/", - "projectStyle": "PackageReference", - "configFilePaths": [ - "/home/runner/.nuget/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "netcoreapp3.1" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp3.1": { - "targetAlias": "netcoreapp3.1", - "projectReferences": {} - } - }, - "warningProperties": { - "warnAsError": [ - "NU1605" - ] - }, - "restoreAuditProperties": { - "enableAudit": "true", - "auditLevel": "low", - "auditMode": "direct" - }, - "SdkAnalysisLevel": "10.0.100" - }, - "frameworks": { - "netcoreapp3.1": { - "targetAlias": "netcoreapp3.1", - "dependencies": { - "DotNetEnv": { - "target": "Package", - "version": "[1.4.0, )" - }, - "Microsoft.AspNetCore.Hosting": { - "target": "Package", - "version": "[2.2.7, )" - }, - "Microsoft.Extensions.DependencyInjection": { - "target": "Package", - "version": "[3.1.8, )" - }, - "Microsoft.Extensions.Logging": { - "target": "Package", - "version": "[3.1.8, )" - }, - "Microsoft.Extensions.Logging.Console": { - "target": "Package", - "version": "[3.1.8, )" - }, - "Yoti": { - "target": "Package", - "version": "[3.6.0, )" - }, - "dotenv.net": { - "target": "Package", - "version": "[2.1.1, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "downloadDependencies": [ - { - "name": "Microsoft.AspNetCore.App.Ref", - "version": "[3.1.10, 3.1.10]" - }, - { - "name": "Microsoft.NETCore.App.Host.linux-x64", - "version": "[3.1.32, 3.1.32]" - }, - { - "name": "Microsoft.NETCore.App.Ref", - "version": "[3.1.0, 3.1.0]" - } - ], - "frameworkReferences": { - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/10.0.100/RuntimeIdentifierGraph.json" - } - } - } - } -} \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props deleted file mode 100644 index 2e49d4b..0000000 --- a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.props +++ /dev/null @@ -1,15 +0,0 @@ - - - - True - NuGet - $(MSBuildThisFileDirectory)project.assets.json - /home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages - /home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages - PackageReference - 7.0.0 - - - - - \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets b/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets deleted file mode 100644 index 3dc06ef..0000000 --- a/dotnet/CoreExample/CoreExample/obj/CoreExample.csproj.nuget.g.targets +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/project.assets.json b/dotnet/CoreExample/CoreExample/obj/project.assets.json deleted file mode 100644 index 28236f4..0000000 --- a/dotnet/CoreExample/CoreExample/obj/project.assets.json +++ /dev/null @@ -1,5889 +0,0 @@ -{ - "version": 3, - "targets": { - ".NETCoreApp,Version=v3.1": { - "dotenv.net/2.1.1": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.4" - }, - "compile": { - "lib/netstandard2.0/dotenv.net.dll": {} - }, - "runtime": { - "lib/netstandard2.0/dotenv.net.dll": {} - } - }, - "DotNetEnv/1.4.0": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1" - }, - "compile": { - "lib/netstandard1.3/DotNetEnv.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetEnv.dll": {} - } - }, - "Google.Protobuf/3.12.3": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/Google.Protobuf.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/netstandard2.0/Google.Protobuf.dll": { - "related": ".pdb;.xml" - } - } - }, - "JsonSubTypes/1.7.0": { - "type": "package", - "dependencies": { - "Newtonsoft.Json": "12.0.3" - }, - "compile": { - "lib/netstandard2.0/JsonSubTypes.dll": {} - }, - "runtime": { - "lib/netstandard2.0/JsonSubTypes.dll": {} - } - }, - "Microsoft.AspNetCore.Hosting/2.2.7": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.Configuration": "2.2.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.2.4", - "Microsoft.Extensions.Configuration.FileExtensions": "2.2.0", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.FileProviders.Physical": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "System.Diagnostics.DiagnosticSource": "4.5.1", - "System.Reflection.Metadata": "1.6.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.Http/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0", - "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.Http.Extensions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Buffers": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { - "related": ".xml" - } - } - }, - "Microsoft.AspNetCore.WebUtilities/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Configuration/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Configuration.Abstractions/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Configuration.Binder/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables/2.2.4": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0", - "Microsoft.Extensions.FileProviders.Physical": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.DependencyInjection/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/3.1.8": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.FileProviders.Physical/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.FileSystemGlobbing": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.FileSystemGlobbing/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Hosting.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Logging/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Binder": "3.1.8", - "Microsoft.Extensions.DependencyInjection": "3.1.8", - "Microsoft.Extensions.Logging.Abstractions": "3.1.8", - "Microsoft.Extensions.Options": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Logging.Abstractions/3.1.8": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Logging.Configuration/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "3.1.8", - "Microsoft.Extensions.Options.ConfigurationExtensions": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Logging.Console/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "3.1.8", - "Microsoft.Extensions.Logging": "3.1.8", - "Microsoft.Extensions.Logging.Configuration": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.ObjectPool/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Options/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8", - "Microsoft.Extensions.Primitives": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/3.1.8": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "3.1.8", - "Microsoft.Extensions.Configuration.Binder": "3.1.8", - "Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.8", - "Microsoft.Extensions.Options": "3.1.8" - }, - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Extensions.Primitives/3.1.8": { - "type": "package", - "compile": { - "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.dll": { - "related": ".xml" - } - } - }, - "Microsoft.Net.Http.Headers/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": { - "related": ".xml" - } - } - }, - "Microsoft.NETCore.Platforms/1.1.1": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.NETCore.Targets/1.1.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.Win32.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/Microsoft.Win32.Primitives.dll": { - "related": ".xml" - } - } - }, - "NETStandard.Library/1.6.1": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, - "Newtonsoft.Json/12.0.3": { - "type": "package", - "compile": { - "lib/netstandard2.0/Newtonsoft.Json.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/Newtonsoft.Json.dll": { - "related": ".xml" - } - } - }, - "NLog/4.7.2": { - "type": "package", - "compile": { - "lib/netstandard2.0/NLog.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/NLog.dll": { - "related": ".xml" - } - } - }, - "Portable.BouncyCastle/1.8.5": { - "type": "package", - "compile": { - "lib/netstandard2.0/BouncyCastle.Crypto.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/BouncyCastle.Crypto.dll": { - "related": ".xml" - } - } - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "debian.8-x64" - } - } - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "fedora.23-x64" - } - } - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "fedora.24-x64" - } - } - }, - "runtime.native.System/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.IO.Compression/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Net.Http/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "type": "package", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "opensuse.13.2-x64" - } - } - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "opensuse.42.1-x64" - } - } - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "type": "package", - "runtimeTargets": { - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib": { - "assetType": "native", - "rid": "osx.10.10-x64" - } - } - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib": { - "assetType": "native", - "rid": "osx.10.10-x64" - } - } - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "rhel.7-x64" - } - } - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.14.04-x64" - } - } - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.16.04-x64" - } - } - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.16.10-x64" - } - } - }, - "System.AppContext/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.AppContext.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.6/System.AppContext.dll": {} - } - }, - "System.Buffers/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.Collections/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.dll": { - "related": ".xml" - } - } - }, - "System.Collections.Concurrent/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.Concurrent.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Collections.Concurrent.dll": {} - } - }, - "System.Console/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Console.dll": { - "related": ".xml" - } - } - }, - "System.Diagnostics.Debug/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Diagnostics.Debug.dll": { - "related": ".xml" - } - } - }, - "System.Diagnostics.DiagnosticSource/4.5.1": { - "type": "package", - "compile": { - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": { - "related": ".xml" - } - } - }, - "System.Diagnostics.Tools/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Diagnostics.Tools.dll": { - "related": ".xml" - } - } - }, - "System.Diagnostics.Tracing/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Diagnostics.Tracing.dll": { - "related": ".xml" - } - } - }, - "System.Globalization/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.dll": { - "related": ".xml" - } - } - }, - "System.Globalization.Calendars/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.Calendars.dll": { - "related": ".xml" - } - } - }, - "System.Globalization.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": { - "related": ".xml" - } - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.IO.dll": { - "related": ".xml" - } - } - }, - "System.IO.Compression/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.Compression.dll": { - "related": ".xml" - } - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO.Compression.ZipFile/4.3.0": { - "type": "package", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.Compression.ZipFile.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.IO.Compression.ZipFile.dll": {} - } - }, - "System.IO.FileSystem/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.FileSystem.dll": { - "related": ".xml" - } - } - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll": {} - } - }, - "System.Linq/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Linq.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.6/System.Linq.dll": {} - } - }, - "System.Linq.Expressions/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Linq.Expressions.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.6/System.Linq.Expressions.dll": {} - } - }, - "System.Memory/4.5.4": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - } - }, - "System.Net.Http/4.3.4": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.1", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "ref/netstandard1.3/System.Net.Http.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.Http.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Primitives.dll": { - "related": ".xml" - } - } - }, - "System.Net.Sockets/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Sockets.dll": { - "related": ".xml" - } - } - }, - "System.ObjectModel/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.ObjectModel.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.ObjectModel.dll": {} - } - }, - "System.Reflection/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Reflection.dll": { - "related": ".xml" - } - } - }, - "System.Reflection.Emit/4.3.0": { - "type": "package", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/_._": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.dll": {} - } - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/_._": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll": {} - } - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/_._": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll": {} - } - }, - "System.Reflection.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Extensions.dll": { - "related": ".xml" - } - } - }, - "System.Reflection.Metadata/1.6.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Reflection.Metadata.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/System.Reflection.Metadata.dll": { - "related": ".xml" - } - } - }, - "System.Reflection.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Primitives.dll": { - "related": ".xml" - } - } - }, - "System.Reflection.TypeExtensions/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/_._": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.5/System.Reflection.TypeExtensions.dll": {} - } - }, - "System.Resources.ResourceManager/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Resources.ResourceManager.dll": { - "related": ".xml" - } - } - }, - "System.Runtime/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "ref/netstandard1.5/System.Runtime.dll": { - "related": ".xml" - } - } - }, - "System.Runtime.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Runtime.Extensions.dll": { - "related": ".xml" - } - } - }, - "System.Runtime.Handles/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Runtime.Handles.dll": { - "related": ".xml" - } - } - }, - "System.Runtime.InteropServices/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netcoreapp1.1/System.Runtime.InteropServices.dll": {} - } - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} - }, - "runtime": { - "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Runtime.Numerics/4.3.0": { - "type": "package", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Runtime.Numerics.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.Numerics.dll": {} - } - }, - "System.Security.Cryptography.Algorithms/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll": {} - }, - "runtimeTargets": { - "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Cng/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Cng.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Csp/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "related": ".xml" - } - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/_._": {} - }, - "runtime": { - "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": { - "assetType": "runtime", - "rid": "unix" - } - } - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} - } - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll": { - "related": ".xml" - } - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Text.Encoding/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Text.Encoding.dll": { - "related": ".xml" - } - } - }, - "System.Text.Encoding.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Text.Encoding.Extensions.dll": { - "related": ".xml" - } - } - }, - "System.Text.Encodings.Web/4.5.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": { - "related": ".xml" - } - } - }, - "System.Text.RegularExpressions/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netcoreapp1.1/System.Text.RegularExpressions.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Text.RegularExpressions.dll": {} - } - }, - "System.Threading/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Threading.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Threading.dll": {} - } - }, - "System.Threading.Tasks/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Threading.Tasks.dll": { - "related": ".xml" - } - } - }, - "System.Threading.Tasks.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "lib/netstandard1.0/_._": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll": { - "related": ".xml" - } - } - }, - "System.Threading.Timer/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.2/System.Threading.Timer.dll": { - "related": ".xml" - } - } - }, - "System.Xml.ReaderWriter/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.ReaderWriter.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Xml.ReaderWriter.dll": {} - } - }, - "System.Xml.XDocument/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.XDocument.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XDocument.dll": {} - } - }, - "Yoti/3.6.0": { - "type": "package", - "dependencies": { - "Google.Protobuf": "3.12.3", - "JsonSubTypes": "1.7.0", - "NLog": "4.7.2", - "Newtonsoft.Json": "12.0.3", - "Portable.BouncyCastle": "1.8.5", - "System.Net.Http": "4.3.4" - }, - "compile": { - "lib/netcoreapp3.1/Yoti.Auth.dll": {} - }, - "runtime": { - "lib/netcoreapp3.1/Yoti.Auth.dll": {} - } - } - } - }, - "libraries": { - "dotenv.net/2.1.1": { - "sha512": "VWP/1PbyjRCLTnfXQNKDtd1yH/zLZKlpuehq2F564XjUXvPeFLJ2YCCY7+2KLkU5bEgumFzirs6Y8gkbRm7w2Q==", - "type": "package", - "path": "dotenv.net/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotenv.net.2.1.1.nupkg.sha512", - "dotenv.net.nuspec", - "lib/netstandard2.0/dotenv.net.dll" - ] - }, - "DotNetEnv/1.4.0": { - "sha512": "ye3WhbO/bHrgl6C6aE8VKKDewdRxEv1tSSnNMbYE1TxROegwkd1KXo/JjpBcC6nR/FbTpSBPZ2RzjxIimgVlSA==", - "type": "package", - "path": "dotnetenv/1.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE", - "dotnetenv.1.4.0.nupkg.sha512", - "dotnetenv.nuspec", - "lib/netstandard1.3/DotNetEnv.dll" - ] - }, - "Google.Protobuf/3.12.3": { - "sha512": "k+Dj0bdOy6/xhIZOthvDS2Ux9y5q6dWw+36n3SBVHYl520+tvcqLOpx6YsgQVy12cuzZbF+r73RZ0fxmpJ8c6A==", - "type": "package", - "path": "google.protobuf/3.12.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "google.protobuf.3.12.3.nupkg.sha512", - "google.protobuf.nuspec", - "lib/net45/Google.Protobuf.dll", - "lib/net45/Google.Protobuf.pdb", - "lib/net45/Google.Protobuf.xml", - "lib/netstandard1.0/Google.Protobuf.dll", - "lib/netstandard1.0/Google.Protobuf.pdb", - "lib/netstandard1.0/Google.Protobuf.xml", - "lib/netstandard2.0/Google.Protobuf.dll", - "lib/netstandard2.0/Google.Protobuf.pdb", - "lib/netstandard2.0/Google.Protobuf.xml" - ] - }, - "JsonSubTypes/1.7.0": { - "sha512": "RY23k6N/BvZ7VSsvgeFihfH42e+q2Lgxd+xydBqu2K6ya4rHd3snZJ2SLk7Xk7pMkl8Pn6lH2PD8uBcrTN0Qhg==", - "type": "package", - "path": "jsonsubtypes/1.7.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE", - "jsonsubtypes.1.7.0.nupkg.sha512", - "jsonsubtypes.nuspec", - "lib/net35/JsonSubTypes.dll", - "lib/net40/JsonSubTypes.dll", - "lib/net45/JsonSubTypes.dll", - "lib/net46/JsonSubTypes.dll", - "lib/net47/JsonSubTypes.dll", - "lib/netstandard1.3/JsonSubTypes.dll", - "lib/netstandard1.4/JsonSubTypes.dll", - "lib/netstandard1.5/JsonSubTypes.dll", - "lib/netstandard1.6/JsonSubTypes.dll", - "lib/netstandard2.0/JsonSubTypes.dll" - ] - }, - "Microsoft.AspNetCore.Hosting/2.2.7": { - "sha512": "O0ZBE53Fa9bVGXykDzvgMFW0Pe1QyPPFg1pazN8l3RUFSWBsDJ9/iD1LHXgADA8+ZD3R/1zkvraPa9SZdievxQ==", - "type": "package", - "path": "microsoft.aspnetcore.hosting/2.2.7", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.xml", - "microsoft.aspnetcore.hosting.2.2.7.nupkg.sha512", - "microsoft.aspnetcore.hosting.nuspec" - ] - }, - "Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": { - "sha512": "ubycklv+ZY7Kutdwuy1W4upWcZ6VFR8WUXU7l7B2+mvbDBBPAcfpi+E+Y5GFe+Q157YfA3C49D2GCjAZc7Mobw==", - "type": "package", - "path": "microsoft.aspnetcore.hosting.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.xml", - "microsoft.aspnetcore.hosting.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.hosting.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { - "sha512": "1PMijw8RMtuQF60SsD/JlKtVfvh4NORAhF4wjysdABhlhTrYmtgssqyncR0Stq5vqtjplZcj6kbT4LRTglt9IQ==", - "type": "package", - "path": "microsoft.aspnetcore.hosting.server.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.xml", - "microsoft.aspnetcore.hosting.server.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.hosting.server.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Http/2.2.0": { - "sha512": "YogBSMotWPAS/X5967pZ+yyWPQkThxhmzAwyCHCSSldzYBkW5W5d6oPfBaPqQOnSHYTpSOSOkpZoAce0vwb6+A==", - "type": "package", - "path": "microsoft.aspnetcore.http/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.xml", - "microsoft.aspnetcore.http.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "sha512": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", - "type": "package", - "path": "microsoft.aspnetcore.http.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.xml", - "microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Extensions/2.2.0": { - "sha512": "2DgZ9rWrJtuR7RYiew01nGRzuQBDaGHGmK56Rk54vsLLsCdzuFUPqbDTJCS1qJQWTbmbIQ9wGIOjpxA1t0l7/w==", - "type": "package", - "path": "microsoft.aspnetcore.http.extensions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.xml", - "microsoft.aspnetcore.http.extensions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.extensions.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "sha512": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", - "type": "package", - "path": "microsoft.aspnetcore.http.features/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.xml", - "microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.features.nuspec" - ] - }, - "Microsoft.AspNetCore.WebUtilities/2.2.0": { - "sha512": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==", - "type": "package", - "path": "microsoft.aspnetcore.webutilities/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.xml", - "microsoft.aspnetcore.webutilities.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.webutilities.nuspec" - ] - }, - "Microsoft.Extensions.Configuration/3.1.8": { - "sha512": "xWvtu/ra8xDOy62ZXzQj1ElmmH3GpZBSKvw4LbfNXKCy+PaziS5Uh0gQ47D4H4w3u+PJfhNWCCGCp9ORNEzkRw==", - "type": "package", - "path": "microsoft.extensions.configuration/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.xml", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.xml", - "microsoft.extensions.configuration.3.1.8.nupkg.sha512", - "microsoft.extensions.configuration.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Abstractions/3.1.8": { - "sha512": "0qbNyxGpuNP/fuQ3FLHesm1Vn/83qYcAgVsi1UQCQN1peY4YH1uiizOh4xbYkQyxiVMD/c/zhiYYv94G0DXSSA==", - "type": "package", - "path": "microsoft.extensions.configuration.abstractions/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Abstractions.xml", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.xml", - "microsoft.extensions.configuration.abstractions.3.1.8.nupkg.sha512", - "microsoft.extensions.configuration.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Binder/3.1.8": { - "sha512": "l/oqIWRM4YF62mlCOrIKGUOCemsaID/lngK2SZEtpYI8LrktpjPd4QzvENWj5GebbLbqOtsFhF6Ko6dgzmUnBw==", - "type": "package", - "path": "microsoft.extensions.configuration.binder/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Configuration.Binder.xml", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.xml", - "microsoft.extensions.configuration.binder.3.1.8.nupkg.sha512", - "microsoft.extensions.configuration.binder.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables/2.2.4": { - "sha512": "Os7uRhp9xwawY5w2tgXw/86YrmAJZl6aHiQVdS9boWybTWPkJOvXXrQ3AGwuldN1W/r+cfnwRe2ePGeFO4zlzg==", - "type": "package", - "path": "microsoft.extensions.configuration.environmentvariables/2.2.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.xml", - "microsoft.extensions.configuration.environmentvariables.2.2.4.nupkg.sha512", - "microsoft.extensions.configuration.environmentvariables.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.2.0": { - "sha512": "H1qCpWBC8Ed4tguTR/qYkbb3F6DI5Su3t8xyFo3/5MzAd8PwPpHzgX8X04KbBxKmk173Pb64x7xMHarczVFQUA==", - "type": "package", - "path": "microsoft.extensions.configuration.fileextensions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.xml", - "microsoft.extensions.configuration.fileextensions.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.fileextensions.nuspec" - ] - }, - "Microsoft.Extensions.DependencyInjection/3.1.8": { - "sha512": "tUpYcVxFqwh8wVD8O+6A8gJnVtl6L4N1Vd9bLJgQSJ0gjBTUQ/eKwJn0LglkkaDU7GAxODDv4eexgZn3QSE0NQ==", - "type": "package", - "path": "microsoft.extensions.dependencyinjection/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/net461/Microsoft.Extensions.DependencyInjection.dll", - "lib/net461/Microsoft.Extensions.DependencyInjection.xml", - "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.DependencyInjection.xml", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.dll", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.xml", - "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.dll", - "lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.xml", - "microsoft.extensions.dependencyinjection.3.1.8.nupkg.sha512", - "microsoft.extensions.dependencyinjection.nuspec" - ] - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/3.1.8": { - "sha512": "YP0kEBkSLTVl3znqZEux+xyJpz5iVNwFZf0OPS7nupdKbojSlO7Fa+JuQjLYpWfpAshaMcznu27tjWzfXRJnOA==", - "type": "package", - "path": "microsoft.extensions.dependencyinjection.abstractions/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", - "microsoft.extensions.dependencyinjection.abstractions.3.1.8.nupkg.sha512", - "microsoft.extensions.dependencyinjection.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.2.0": { - "sha512": "EcnaSsPTqx2MGnHrmWOD0ugbuuqVT8iICqSqPzi45V5/MA1LjUNb0kwgcxBGqizV1R+WeBK7/Gw25Jzkyk9bIw==", - "type": "package", - "path": "microsoft.extensions.fileproviders.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.xml", - "microsoft.extensions.fileproviders.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.fileproviders.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Physical/2.2.0": { - "sha512": "tbDHZnBJkjYd9NjlRZ9ondDiv1Te3KYCTW2RWpR1B0e1Z8+EnFRo7qNnHkkSCixLdlPZzhjlX24d/PixQ7w2dA==", - "type": "package", - "path": "microsoft.extensions.fileproviders.physical/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.xml", - "microsoft.extensions.fileproviders.physical.2.2.0.nupkg.sha512", - "microsoft.extensions.fileproviders.physical.nuspec" - ] - }, - "Microsoft.Extensions.FileSystemGlobbing/2.2.0": { - "sha512": "ZSsHZp3PyW6vk37tDEdypjgGlNtpJ0EixBMOfUod2Thx7GtwfFSAQXUQx8a8BN8vfWKGGMbp7jPWdoHx/At4wQ==", - "type": "package", - "path": "microsoft.extensions.filesystemglobbing/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.xml", - "microsoft.extensions.filesystemglobbing.2.2.0.nupkg.sha512", - "microsoft.extensions.filesystemglobbing.nuspec" - ] - }, - "Microsoft.Extensions.Hosting.Abstractions/2.2.0": { - "sha512": "+k4AEn68HOJat5gj1TWa6X28WlirNQO9sPIIeQbia+91n03esEtMSSoekSTpMjUzjqtJWQN3McVx0GvSPFHF/Q==", - "type": "package", - "path": "microsoft.extensions.hosting.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.xml", - "microsoft.extensions.hosting.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.hosting.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Logging/3.1.8": { - "sha512": "Bch88WGwrgJUabSOiTbPgne/jkCcWTyP97db8GWzQH9RcGi6TThiRm8ggsD+OXBW2UBwAYx1Zb1ns1elsMiomQ==", - "type": "package", - "path": "microsoft.extensions.logging/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.xml", - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.xml", - "microsoft.extensions.logging.3.1.8.nupkg.sha512", - "microsoft.extensions.logging.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Abstractions/3.1.8": { - "sha512": "LxQPR/KE4P9nx304VcFipWPcW8ZOZOGHuiYlG0ncAQJItogDzR9nyYUNvziLObx2MfX2Z9iCTdAqEtoImaQOYg==", - "type": "package", - "path": "microsoft.extensions.logging.abstractions/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.xml", - "microsoft.extensions.logging.abstractions.3.1.8.nupkg.sha512", - "microsoft.extensions.logging.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Configuration/3.1.8": { - "sha512": "tfvQYDDwc3ni8VTXQavINMHRUIsM8fQ8gBQTs98mt1QFE3YIVm8XzhqPZMBBlLnXTESymb8HctM2fMnU8qC8Rg==", - "type": "package", - "path": "microsoft.extensions.logging.configuration/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Configuration.xml", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.xml", - "microsoft.extensions.logging.configuration.3.1.8.nupkg.sha512", - "microsoft.extensions.logging.configuration.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Console/3.1.8": { - "sha512": "7JCZuqty78ZDwoXOGXGUXMC44rpocfeaa/48ONY7neKhCh2Oml/faW3PANPCdwy6M3TicmU03kIOhyrw3dQ2Eg==", - "type": "package", - "path": "microsoft.extensions.logging.console/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Logging.Console.xml", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.xml", - "microsoft.extensions.logging.console.3.1.8.nupkg.sha512", - "microsoft.extensions.logging.console.nuspec" - ] - }, - "Microsoft.Extensions.ObjectPool/2.2.0": { - "sha512": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g==", - "type": "package", - "path": "microsoft.extensions.objectpool/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll", - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.xml", - "microsoft.extensions.objectpool.2.2.0.nupkg.sha512", - "microsoft.extensions.objectpool.nuspec" - ] - }, - "Microsoft.Extensions.Options/3.1.8": { - "sha512": "mpkwjNg5sr1XHEJwVS8G1w6dsh5/72vQOOe4aqhg012j93m8OOmfyIBwoQN4SE0KRRS+fatdW3qqUrHbRwlWOA==", - "type": "package", - "path": "microsoft.extensions.options/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Options.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Options.xml", - "lib/netstandard2.0/Microsoft.Extensions.Options.dll", - "lib/netstandard2.0/Microsoft.Extensions.Options.xml", - "microsoft.extensions.options.3.1.8.nupkg.sha512", - "microsoft.extensions.options.nuspec" - ] - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/3.1.8": { - "sha512": "/q7OhcsgDq6cPqg03nv55QqLt8o/OAvrVkd/w6h0YNasZ4C/Lxpx6I0DsnIH0MB5ORnqCyhmeyv1hFqOeehJng==", - "type": "package", - "path": "microsoft.extensions.options.configurationextensions/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Options.ConfigurationExtensions.xml", - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.xml", - "microsoft.extensions.options.configurationextensions.3.1.8.nupkg.sha512", - "microsoft.extensions.options.configurationextensions.nuspec" - ] - }, - "Microsoft.Extensions.Primitives/3.1.8": { - "sha512": "XcIoXQhT0kwnEhOKv/LmpWR6yF6QWmBTy9Fcsz4aHuCOgTJ7Zd23ELtUA4BfwlYoFlSedavS+vURz9tNekd44g==", - "type": "package", - "path": "microsoft.extensions.primitives/3.1.8", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Icon.png", - "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.dll", - "lib/netcoreapp3.1/Microsoft.Extensions.Primitives.xml", - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll", - "lib/netstandard2.0/Microsoft.Extensions.Primitives.xml", - "microsoft.extensions.primitives.3.1.8.nupkg.sha512", - "microsoft.extensions.primitives.nuspec" - ] - }, - "Microsoft.Net.Http.Headers/2.2.0": { - "sha512": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==", - "type": "package", - "path": "microsoft.net.http.headers/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll", - "lib/netstandard2.0/Microsoft.Net.Http.Headers.xml", - "microsoft.net.http.headers.2.2.0.nupkg.sha512", - "microsoft.net.http.headers.nuspec" - ] - }, - "Microsoft.NETCore.Platforms/1.1.1": { - "sha512": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==", - "type": "package", - "path": "microsoft.netcore.platforms/1.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "microsoft.netcore.platforms.1.1.1.nupkg.sha512", - "microsoft.netcore.platforms.nuspec", - "runtime.json" - ] - }, - "Microsoft.NETCore.Targets/1.1.0": { - "sha512": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==", - "type": "package", - "path": "microsoft.netcore.targets/1.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "microsoft.netcore.targets.1.1.0.nupkg.sha512", - "microsoft.netcore.targets.nuspec", - "runtime.json" - ] - }, - "Microsoft.Win32.Primitives/4.3.0": { - "sha512": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", - "type": "package", - "path": "microsoft.win32.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/Microsoft.Win32.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "microsoft.win32.primitives.4.3.0.nupkg.sha512", - "microsoft.win32.primitives.nuspec", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/Microsoft.Win32.Primitives.dll", - "ref/netstandard1.3/Microsoft.Win32.Primitives.dll", - "ref/netstandard1.3/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/de/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/es/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/fr/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/it/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ja/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ko/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ru/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/zh-hans/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/zh-hant/Microsoft.Win32.Primitives.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._" - ] - }, - "NETStandard.Library/1.6.1": { - "sha512": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", - "type": "package", - "path": "netstandard.library/1.6.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "netstandard.library.1.6.1.nupkg.sha512", - "netstandard.library.nuspec" - ] - }, - "Newtonsoft.Json/12.0.3": { - "sha512": "6mgjfnRB4jKMlzHSl+VD+oUc1IebOZabkbyWj2RiTgWwYPPuaK1H97G1sHqGwPlS5npiF5Q0OrxN1wni2n5QWg==", - "type": "package", - "path": "newtonsoft.json/12.0.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net20/Newtonsoft.Json.dll", - "lib/net20/Newtonsoft.Json.xml", - "lib/net35/Newtonsoft.Json.dll", - "lib/net35/Newtonsoft.Json.xml", - "lib/net40/Newtonsoft.Json.dll", - "lib/net40/Newtonsoft.Json.xml", - "lib/net45/Newtonsoft.Json.dll", - "lib/net45/Newtonsoft.Json.xml", - "lib/netstandard1.0/Newtonsoft.Json.dll", - "lib/netstandard1.0/Newtonsoft.Json.xml", - "lib/netstandard1.3/Newtonsoft.Json.dll", - "lib/netstandard1.3/Newtonsoft.Json.xml", - "lib/netstandard2.0/Newtonsoft.Json.dll", - "lib/netstandard2.0/Newtonsoft.Json.xml", - "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll", - "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml", - "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll", - "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml", - "newtonsoft.json.12.0.3.nupkg.sha512", - "newtonsoft.json.nuspec", - "packageIcon.png" - ] - }, - "NLog/4.7.2": { - "sha512": "tfZNTOYr0TwvJNrXsJsVdZPSgIpYN1rXHnAZAadeaIbz+v18agNCCxgZXnXQqNKNizkogJ68zYTNKavyQFWwrg==", - "type": "package", - "path": "nlog/4.7.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/monoandroid44/NLog.dll", - "lib/monoandroid44/NLog.xml", - "lib/net35/NLog.dll", - "lib/net35/NLog.xml", - "lib/net40-client/NLog.dll", - "lib/net40-client/NLog.xml", - "lib/net45/NLog.dll", - "lib/net45/NLog.xml", - "lib/netstandard1.3/NLog.dll", - "lib/netstandard1.3/NLog.xml", - "lib/netstandard1.5/NLog.dll", - "lib/netstandard1.5/NLog.xml", - "lib/netstandard2.0/NLog.dll", - "lib/netstandard2.0/NLog.xml", - "lib/sl4/NLog.dll", - "lib/sl4/NLog.xml", - "lib/sl5/NLog.dll", - "lib/sl5/NLog.xml", - "lib/wp8/NLog.dll", - "lib/wp8/NLog.xml", - "lib/xamarinios10/NLog.dll", - "lib/xamarinios10/NLog.xml", - "nlog.4.7.2.nupkg.sha512", - "nlog.nuspec" - ] - }, - "Portable.BouncyCastle/1.8.5": { - "sha512": "EaCgmntbH1sOzemRTqyXSqYjB6pLH7VCYHhhDYZ59guHSD5qPwhIYa7kfy0QUlmTRt9IXhaXdFhNuBUArp70Ng==", - "type": "package", - "path": "portable.bouncycastle/1.8.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/BouncyCastle.Crypto.dll", - "lib/net40/BouncyCastle.Crypto.xml", - "lib/netstandard1.0/BouncyCastle.Crypto.dll", - "lib/netstandard1.0/BouncyCastle.Crypto.xml", - "lib/netstandard1.3/BouncyCastle.Crypto.dll", - "lib/netstandard1.3/BouncyCastle.Crypto.xml", - "lib/netstandard2.0/BouncyCastle.Crypto.dll", - "lib/netstandard2.0/BouncyCastle.Crypto.xml", - "portable.bouncycastle.1.8.5.nupkg.sha512", - "portable.bouncycastle.nuspec" - ] - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==", - "type": "package", - "path": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==", - "type": "package", - "path": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==", - "type": "package", - "path": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.native.System/4.3.0": { - "sha512": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "type": "package", - "path": "runtime.native.system/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.4.3.0.nupkg.sha512", - "runtime.native.system.nuspec" - ] - }, - "runtime.native.System.IO.Compression/4.3.0": { - "sha512": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "type": "package", - "path": "runtime.native.system.io.compression/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.io.compression.4.3.0.nupkg.sha512", - "runtime.native.system.io.compression.nuspec" - ] - }, - "runtime.native.System.Net.Http/4.3.0": { - "sha512": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "type": "package", - "path": "runtime.native.system.net.http/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.net.http.4.3.0.nupkg.sha512", - "runtime.native.system.net.http.nuspec" - ] - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "sha512": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "type": "package", - "path": "runtime.native.system.security.cryptography.apple/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", - "runtime.native.system.security.cryptography.apple.nuspec" - ] - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", - "type": "package", - "path": "runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.native.system.security.cryptography.openssl.nuspec" - ] - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==", - "type": "package", - "path": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==", - "type": "package", - "path": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "sha512": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==", - "type": "package", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.nuspec", - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib" - ] - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==", - "type": "package", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib" - ] - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==", - "type": "package", - "path": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==", - "type": "package", - "path": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==", - "type": "package", - "path": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==", - "type": "package", - "path": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "System.AppContext/4.3.0": { - "sha512": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "type": "package", - "path": "system.appcontext/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.AppContext.dll", - "lib/net463/System.AppContext.dll", - "lib/netcore50/System.AppContext.dll", - "lib/netstandard1.6/System.AppContext.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.AppContext.dll", - "ref/net463/System.AppContext.dll", - "ref/netstandard/_._", - "ref/netstandard1.3/System.AppContext.dll", - "ref/netstandard1.3/System.AppContext.xml", - "ref/netstandard1.3/de/System.AppContext.xml", - "ref/netstandard1.3/es/System.AppContext.xml", - "ref/netstandard1.3/fr/System.AppContext.xml", - "ref/netstandard1.3/it/System.AppContext.xml", - "ref/netstandard1.3/ja/System.AppContext.xml", - "ref/netstandard1.3/ko/System.AppContext.xml", - "ref/netstandard1.3/ru/System.AppContext.xml", - "ref/netstandard1.3/zh-hans/System.AppContext.xml", - "ref/netstandard1.3/zh-hant/System.AppContext.xml", - "ref/netstandard1.6/System.AppContext.dll", - "ref/netstandard1.6/System.AppContext.xml", - "ref/netstandard1.6/de/System.AppContext.xml", - "ref/netstandard1.6/es/System.AppContext.xml", - "ref/netstandard1.6/fr/System.AppContext.xml", - "ref/netstandard1.6/it/System.AppContext.xml", - "ref/netstandard1.6/ja/System.AppContext.xml", - "ref/netstandard1.6/ko/System.AppContext.xml", - "ref/netstandard1.6/ru/System.AppContext.xml", - "ref/netstandard1.6/zh-hans/System.AppContext.xml", - "ref/netstandard1.6/zh-hant/System.AppContext.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.AppContext.dll", - "system.appcontext.4.3.0.nupkg.sha512", - "system.appcontext.nuspec" - ] - }, - "System.Buffers/4.5.0": { - "sha512": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A==", - "type": "package", - "path": "system.buffers/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.1/System.Buffers.dll", - "lib/netstandard1.1/System.Buffers.xml", - "lib/netstandard2.0/System.Buffers.dll", - "lib/netstandard2.0/System.Buffers.xml", - "lib/uap10.0.16299/_._", - "ref/net45/System.Buffers.dll", - "ref/net45/System.Buffers.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.1/System.Buffers.dll", - "ref/netstandard1.1/System.Buffers.xml", - "ref/netstandard2.0/System.Buffers.dll", - "ref/netstandard2.0/System.Buffers.xml", - "ref/uap10.0.16299/_._", - "system.buffers.4.5.0.nupkg.sha512", - "system.buffers.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Collections/4.3.0": { - "sha512": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "type": "package", - "path": "system.collections/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Collections.dll", - "ref/netcore50/System.Collections.xml", - "ref/netcore50/de/System.Collections.xml", - "ref/netcore50/es/System.Collections.xml", - "ref/netcore50/fr/System.Collections.xml", - "ref/netcore50/it/System.Collections.xml", - "ref/netcore50/ja/System.Collections.xml", - "ref/netcore50/ko/System.Collections.xml", - "ref/netcore50/ru/System.Collections.xml", - "ref/netcore50/zh-hans/System.Collections.xml", - "ref/netcore50/zh-hant/System.Collections.xml", - "ref/netstandard1.0/System.Collections.dll", - "ref/netstandard1.0/System.Collections.xml", - "ref/netstandard1.0/de/System.Collections.xml", - "ref/netstandard1.0/es/System.Collections.xml", - "ref/netstandard1.0/fr/System.Collections.xml", - "ref/netstandard1.0/it/System.Collections.xml", - "ref/netstandard1.0/ja/System.Collections.xml", - "ref/netstandard1.0/ko/System.Collections.xml", - "ref/netstandard1.0/ru/System.Collections.xml", - "ref/netstandard1.0/zh-hans/System.Collections.xml", - "ref/netstandard1.0/zh-hant/System.Collections.xml", - "ref/netstandard1.3/System.Collections.dll", - "ref/netstandard1.3/System.Collections.xml", - "ref/netstandard1.3/de/System.Collections.xml", - "ref/netstandard1.3/es/System.Collections.xml", - "ref/netstandard1.3/fr/System.Collections.xml", - "ref/netstandard1.3/it/System.Collections.xml", - "ref/netstandard1.3/ja/System.Collections.xml", - "ref/netstandard1.3/ko/System.Collections.xml", - "ref/netstandard1.3/ru/System.Collections.xml", - "ref/netstandard1.3/zh-hans/System.Collections.xml", - "ref/netstandard1.3/zh-hant/System.Collections.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.4.3.0.nupkg.sha512", - "system.collections.nuspec" - ] - }, - "System.Collections.Concurrent/4.3.0": { - "sha512": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "type": "package", - "path": "system.collections.concurrent/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Collections.Concurrent.dll", - "lib/netstandard1.3/System.Collections.Concurrent.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Collections.Concurrent.dll", - "ref/netcore50/System.Collections.Concurrent.xml", - "ref/netcore50/de/System.Collections.Concurrent.xml", - "ref/netcore50/es/System.Collections.Concurrent.xml", - "ref/netcore50/fr/System.Collections.Concurrent.xml", - "ref/netcore50/it/System.Collections.Concurrent.xml", - "ref/netcore50/ja/System.Collections.Concurrent.xml", - "ref/netcore50/ko/System.Collections.Concurrent.xml", - "ref/netcore50/ru/System.Collections.Concurrent.xml", - "ref/netcore50/zh-hans/System.Collections.Concurrent.xml", - "ref/netcore50/zh-hant/System.Collections.Concurrent.xml", - "ref/netstandard1.1/System.Collections.Concurrent.dll", - "ref/netstandard1.1/System.Collections.Concurrent.xml", - "ref/netstandard1.1/de/System.Collections.Concurrent.xml", - "ref/netstandard1.1/es/System.Collections.Concurrent.xml", - "ref/netstandard1.1/fr/System.Collections.Concurrent.xml", - "ref/netstandard1.1/it/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ja/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ko/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ru/System.Collections.Concurrent.xml", - "ref/netstandard1.1/zh-hans/System.Collections.Concurrent.xml", - "ref/netstandard1.1/zh-hant/System.Collections.Concurrent.xml", - "ref/netstandard1.3/System.Collections.Concurrent.dll", - "ref/netstandard1.3/System.Collections.Concurrent.xml", - "ref/netstandard1.3/de/System.Collections.Concurrent.xml", - "ref/netstandard1.3/es/System.Collections.Concurrent.xml", - "ref/netstandard1.3/fr/System.Collections.Concurrent.xml", - "ref/netstandard1.3/it/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ja/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ko/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ru/System.Collections.Concurrent.xml", - "ref/netstandard1.3/zh-hans/System.Collections.Concurrent.xml", - "ref/netstandard1.3/zh-hant/System.Collections.Concurrent.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.concurrent.4.3.0.nupkg.sha512", - "system.collections.concurrent.nuspec" - ] - }, - "System.Console/4.3.0": { - "sha512": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "type": "package", - "path": "system.console/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Console.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Console.dll", - "ref/netstandard1.3/System.Console.dll", - "ref/netstandard1.3/System.Console.xml", - "ref/netstandard1.3/de/System.Console.xml", - "ref/netstandard1.3/es/System.Console.xml", - "ref/netstandard1.3/fr/System.Console.xml", - "ref/netstandard1.3/it/System.Console.xml", - "ref/netstandard1.3/ja/System.Console.xml", - "ref/netstandard1.3/ko/System.Console.xml", - "ref/netstandard1.3/ru/System.Console.xml", - "ref/netstandard1.3/zh-hans/System.Console.xml", - "ref/netstandard1.3/zh-hant/System.Console.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.console.4.3.0.nupkg.sha512", - "system.console.nuspec" - ] - }, - "System.Diagnostics.Debug/4.3.0": { - "sha512": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "type": "package", - "path": "system.diagnostics.debug/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Debug.dll", - "ref/netcore50/System.Diagnostics.Debug.xml", - "ref/netcore50/de/System.Diagnostics.Debug.xml", - "ref/netcore50/es/System.Diagnostics.Debug.xml", - "ref/netcore50/fr/System.Diagnostics.Debug.xml", - "ref/netcore50/it/System.Diagnostics.Debug.xml", - "ref/netcore50/ja/System.Diagnostics.Debug.xml", - "ref/netcore50/ko/System.Diagnostics.Debug.xml", - "ref/netcore50/ru/System.Diagnostics.Debug.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Debug.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/System.Diagnostics.Debug.dll", - "ref/netstandard1.0/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/de/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/es/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/it/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/System.Diagnostics.Debug.dll", - "ref/netstandard1.3/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/de/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/es/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/it/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Debug.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.debug.4.3.0.nupkg.sha512", - "system.diagnostics.debug.nuspec" - ] - }, - "System.Diagnostics.DiagnosticSource/4.5.1": { - "sha512": "zCno/m44ymWhgLFh7tELDG9587q0l/EynPM0m4KgLaWQbz/TEKvNRX2YT5ip2qXW/uayifQ2ZqbnErsKJ4lYrQ==", - "type": "package", - "path": "system.diagnostics.diagnosticsource/4.5.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net45/System.Diagnostics.DiagnosticSource.dll", - "lib/net45/System.Diagnostics.DiagnosticSource.xml", - "lib/net46/System.Diagnostics.DiagnosticSource.dll", - "lib/net46/System.Diagnostics.DiagnosticSource.xml", - "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.dll", - "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.xml", - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll", - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.xml", - "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.dll", - "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.xml", - "system.diagnostics.diagnosticsource.4.5.1.nupkg.sha512", - "system.diagnostics.diagnosticsource.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Diagnostics.Tools/4.3.0": { - "sha512": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "type": "package", - "path": "system.diagnostics.tools/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Tools.dll", - "ref/netcore50/System.Diagnostics.Tools.xml", - "ref/netcore50/de/System.Diagnostics.Tools.xml", - "ref/netcore50/es/System.Diagnostics.Tools.xml", - "ref/netcore50/fr/System.Diagnostics.Tools.xml", - "ref/netcore50/it/System.Diagnostics.Tools.xml", - "ref/netcore50/ja/System.Diagnostics.Tools.xml", - "ref/netcore50/ko/System.Diagnostics.Tools.xml", - "ref/netcore50/ru/System.Diagnostics.Tools.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Tools.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/System.Diagnostics.Tools.dll", - "ref/netstandard1.0/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/de/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/es/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/it/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Tools.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.tools.4.3.0.nupkg.sha512", - "system.diagnostics.tools.nuspec" - ] - }, - "System.Diagnostics.Tracing/4.3.0": { - "sha512": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "type": "package", - "path": "system.diagnostics.tracing/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Diagnostics.Tracing.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Diagnostics.Tracing.dll", - "ref/netcore50/System.Diagnostics.Tracing.dll", - "ref/netcore50/System.Diagnostics.Tracing.xml", - "ref/netcore50/de/System.Diagnostics.Tracing.xml", - "ref/netcore50/es/System.Diagnostics.Tracing.xml", - "ref/netcore50/fr/System.Diagnostics.Tracing.xml", - "ref/netcore50/it/System.Diagnostics.Tracing.xml", - "ref/netcore50/ja/System.Diagnostics.Tracing.xml", - "ref/netcore50/ko/System.Diagnostics.Tracing.xml", - "ref/netcore50/ru/System.Diagnostics.Tracing.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/System.Diagnostics.Tracing.dll", - "ref/netstandard1.1/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/System.Diagnostics.Tracing.dll", - "ref/netstandard1.2/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/System.Diagnostics.Tracing.dll", - "ref/netstandard1.3/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/System.Diagnostics.Tracing.dll", - "ref/netstandard1.5/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/zh-hant/System.Diagnostics.Tracing.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.tracing.4.3.0.nupkg.sha512", - "system.diagnostics.tracing.nuspec" - ] - }, - "System.Globalization/4.3.0": { - "sha512": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "type": "package", - "path": "system.globalization/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Globalization.dll", - "ref/netcore50/System.Globalization.xml", - "ref/netcore50/de/System.Globalization.xml", - "ref/netcore50/es/System.Globalization.xml", - "ref/netcore50/fr/System.Globalization.xml", - "ref/netcore50/it/System.Globalization.xml", - "ref/netcore50/ja/System.Globalization.xml", - "ref/netcore50/ko/System.Globalization.xml", - "ref/netcore50/ru/System.Globalization.xml", - "ref/netcore50/zh-hans/System.Globalization.xml", - "ref/netcore50/zh-hant/System.Globalization.xml", - "ref/netstandard1.0/System.Globalization.dll", - "ref/netstandard1.0/System.Globalization.xml", - "ref/netstandard1.0/de/System.Globalization.xml", - "ref/netstandard1.0/es/System.Globalization.xml", - "ref/netstandard1.0/fr/System.Globalization.xml", - "ref/netstandard1.0/it/System.Globalization.xml", - "ref/netstandard1.0/ja/System.Globalization.xml", - "ref/netstandard1.0/ko/System.Globalization.xml", - "ref/netstandard1.0/ru/System.Globalization.xml", - "ref/netstandard1.0/zh-hans/System.Globalization.xml", - "ref/netstandard1.0/zh-hant/System.Globalization.xml", - "ref/netstandard1.3/System.Globalization.dll", - "ref/netstandard1.3/System.Globalization.xml", - "ref/netstandard1.3/de/System.Globalization.xml", - "ref/netstandard1.3/es/System.Globalization.xml", - "ref/netstandard1.3/fr/System.Globalization.xml", - "ref/netstandard1.3/it/System.Globalization.xml", - "ref/netstandard1.3/ja/System.Globalization.xml", - "ref/netstandard1.3/ko/System.Globalization.xml", - "ref/netstandard1.3/ru/System.Globalization.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.globalization.4.3.0.nupkg.sha512", - "system.globalization.nuspec" - ] - }, - "System.Globalization.Calendars/4.3.0": { - "sha512": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "type": "package", - "path": "system.globalization.calendars/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Globalization.Calendars.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Globalization.Calendars.dll", - "ref/netstandard1.3/System.Globalization.Calendars.dll", - "ref/netstandard1.3/System.Globalization.Calendars.xml", - "ref/netstandard1.3/de/System.Globalization.Calendars.xml", - "ref/netstandard1.3/es/System.Globalization.Calendars.xml", - "ref/netstandard1.3/fr/System.Globalization.Calendars.xml", - "ref/netstandard1.3/it/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ja/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ko/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ru/System.Globalization.Calendars.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.Calendars.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.Calendars.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.globalization.calendars.4.3.0.nupkg.sha512", - "system.globalization.calendars.nuspec" - ] - }, - "System.Globalization.Extensions/4.3.0": { - "sha512": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "type": "package", - "path": "system.globalization.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Globalization.Extensions.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Globalization.Extensions.dll", - "ref/netstandard1.3/System.Globalization.Extensions.dll", - "ref/netstandard1.3/System.Globalization.Extensions.xml", - "ref/netstandard1.3/de/System.Globalization.Extensions.xml", - "ref/netstandard1.3/es/System.Globalization.Extensions.xml", - "ref/netstandard1.3/fr/System.Globalization.Extensions.xml", - "ref/netstandard1.3/it/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ja/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ko/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ru/System.Globalization.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.Extensions.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll", - "runtimes/win/lib/net46/System.Globalization.Extensions.dll", - "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll", - "system.globalization.extensions.4.3.0.nupkg.sha512", - "system.globalization.extensions.nuspec" - ] - }, - "System.IO/4.3.0": { - "sha512": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "type": "package", - "path": "system.io/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.IO.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.IO.dll", - "ref/netcore50/System.IO.dll", - "ref/netcore50/System.IO.xml", - "ref/netcore50/de/System.IO.xml", - "ref/netcore50/es/System.IO.xml", - "ref/netcore50/fr/System.IO.xml", - "ref/netcore50/it/System.IO.xml", - "ref/netcore50/ja/System.IO.xml", - "ref/netcore50/ko/System.IO.xml", - "ref/netcore50/ru/System.IO.xml", - "ref/netcore50/zh-hans/System.IO.xml", - "ref/netcore50/zh-hant/System.IO.xml", - "ref/netstandard1.0/System.IO.dll", - "ref/netstandard1.0/System.IO.xml", - "ref/netstandard1.0/de/System.IO.xml", - "ref/netstandard1.0/es/System.IO.xml", - "ref/netstandard1.0/fr/System.IO.xml", - "ref/netstandard1.0/it/System.IO.xml", - "ref/netstandard1.0/ja/System.IO.xml", - "ref/netstandard1.0/ko/System.IO.xml", - "ref/netstandard1.0/ru/System.IO.xml", - "ref/netstandard1.0/zh-hans/System.IO.xml", - "ref/netstandard1.0/zh-hant/System.IO.xml", - "ref/netstandard1.3/System.IO.dll", - "ref/netstandard1.3/System.IO.xml", - "ref/netstandard1.3/de/System.IO.xml", - "ref/netstandard1.3/es/System.IO.xml", - "ref/netstandard1.3/fr/System.IO.xml", - "ref/netstandard1.3/it/System.IO.xml", - "ref/netstandard1.3/ja/System.IO.xml", - "ref/netstandard1.3/ko/System.IO.xml", - "ref/netstandard1.3/ru/System.IO.xml", - "ref/netstandard1.3/zh-hans/System.IO.xml", - "ref/netstandard1.3/zh-hant/System.IO.xml", - "ref/netstandard1.5/System.IO.dll", - "ref/netstandard1.5/System.IO.xml", - "ref/netstandard1.5/de/System.IO.xml", - "ref/netstandard1.5/es/System.IO.xml", - "ref/netstandard1.5/fr/System.IO.xml", - "ref/netstandard1.5/it/System.IO.xml", - "ref/netstandard1.5/ja/System.IO.xml", - "ref/netstandard1.5/ko/System.IO.xml", - "ref/netstandard1.5/ru/System.IO.xml", - "ref/netstandard1.5/zh-hans/System.IO.xml", - "ref/netstandard1.5/zh-hant/System.IO.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.4.3.0.nupkg.sha512", - "system.io.nuspec" - ] - }, - "System.IO.Compression/4.3.0": { - "sha512": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "type": "package", - "path": "system.io.compression/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.IO.Compression.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.IO.Compression.dll", - "ref/netcore50/System.IO.Compression.dll", - "ref/netcore50/System.IO.Compression.xml", - "ref/netcore50/de/System.IO.Compression.xml", - "ref/netcore50/es/System.IO.Compression.xml", - "ref/netcore50/fr/System.IO.Compression.xml", - "ref/netcore50/it/System.IO.Compression.xml", - "ref/netcore50/ja/System.IO.Compression.xml", - "ref/netcore50/ko/System.IO.Compression.xml", - "ref/netcore50/ru/System.IO.Compression.xml", - "ref/netcore50/zh-hans/System.IO.Compression.xml", - "ref/netcore50/zh-hant/System.IO.Compression.xml", - "ref/netstandard1.1/System.IO.Compression.dll", - "ref/netstandard1.1/System.IO.Compression.xml", - "ref/netstandard1.1/de/System.IO.Compression.xml", - "ref/netstandard1.1/es/System.IO.Compression.xml", - "ref/netstandard1.1/fr/System.IO.Compression.xml", - "ref/netstandard1.1/it/System.IO.Compression.xml", - "ref/netstandard1.1/ja/System.IO.Compression.xml", - "ref/netstandard1.1/ko/System.IO.Compression.xml", - "ref/netstandard1.1/ru/System.IO.Compression.xml", - "ref/netstandard1.1/zh-hans/System.IO.Compression.xml", - "ref/netstandard1.1/zh-hant/System.IO.Compression.xml", - "ref/netstandard1.3/System.IO.Compression.dll", - "ref/netstandard1.3/System.IO.Compression.xml", - "ref/netstandard1.3/de/System.IO.Compression.xml", - "ref/netstandard1.3/es/System.IO.Compression.xml", - "ref/netstandard1.3/fr/System.IO.Compression.xml", - "ref/netstandard1.3/it/System.IO.Compression.xml", - "ref/netstandard1.3/ja/System.IO.Compression.xml", - "ref/netstandard1.3/ko/System.IO.Compression.xml", - "ref/netstandard1.3/ru/System.IO.Compression.xml", - "ref/netstandard1.3/zh-hans/System.IO.Compression.xml", - "ref/netstandard1.3/zh-hant/System.IO.Compression.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll", - "runtimes/win/lib/net46/System.IO.Compression.dll", - "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll", - "system.io.compression.4.3.0.nupkg.sha512", - "system.io.compression.nuspec" - ] - }, - "System.IO.Compression.ZipFile/4.3.0": { - "sha512": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", - "type": "package", - "path": "system.io.compression.zipfile/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.Compression.ZipFile.dll", - "lib/netstandard1.3/System.IO.Compression.ZipFile.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.Compression.ZipFile.dll", - "ref/netstandard1.3/System.IO.Compression.ZipFile.dll", - "ref/netstandard1.3/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/de/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/es/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/fr/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/it/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ja/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ko/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ru/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/zh-hans/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/zh-hant/System.IO.Compression.ZipFile.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.compression.zipfile.4.3.0.nupkg.sha512", - "system.io.compression.zipfile.nuspec" - ] - }, - "System.IO.FileSystem/4.3.0": { - "sha512": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "type": "package", - "path": "system.io.filesystem/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.FileSystem.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.FileSystem.dll", - "ref/netstandard1.3/System.IO.FileSystem.dll", - "ref/netstandard1.3/System.IO.FileSystem.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.filesystem.4.3.0.nupkg.sha512", - "system.io.filesystem.nuspec" - ] - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "sha512": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "type": "package", - "path": "system.io.filesystem.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.FileSystem.Primitives.dll", - "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.FileSystem.Primitives.dll", - "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll", - "ref/netstandard1.3/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.Primitives.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.filesystem.primitives.4.3.0.nupkg.sha512", - "system.io.filesystem.primitives.nuspec" - ] - }, - "System.Linq/4.3.0": { - "sha512": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "type": "package", - "path": "system.linq/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Linq.dll", - "lib/netcore50/System.Linq.dll", - "lib/netstandard1.6/System.Linq.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Linq.dll", - "ref/netcore50/System.Linq.dll", - "ref/netcore50/System.Linq.xml", - "ref/netcore50/de/System.Linq.xml", - "ref/netcore50/es/System.Linq.xml", - "ref/netcore50/fr/System.Linq.xml", - "ref/netcore50/it/System.Linq.xml", - "ref/netcore50/ja/System.Linq.xml", - "ref/netcore50/ko/System.Linq.xml", - "ref/netcore50/ru/System.Linq.xml", - "ref/netcore50/zh-hans/System.Linq.xml", - "ref/netcore50/zh-hant/System.Linq.xml", - "ref/netstandard1.0/System.Linq.dll", - "ref/netstandard1.0/System.Linq.xml", - "ref/netstandard1.0/de/System.Linq.xml", - "ref/netstandard1.0/es/System.Linq.xml", - "ref/netstandard1.0/fr/System.Linq.xml", - "ref/netstandard1.0/it/System.Linq.xml", - "ref/netstandard1.0/ja/System.Linq.xml", - "ref/netstandard1.0/ko/System.Linq.xml", - "ref/netstandard1.0/ru/System.Linq.xml", - "ref/netstandard1.0/zh-hans/System.Linq.xml", - "ref/netstandard1.0/zh-hant/System.Linq.xml", - "ref/netstandard1.6/System.Linq.dll", - "ref/netstandard1.6/System.Linq.xml", - "ref/netstandard1.6/de/System.Linq.xml", - "ref/netstandard1.6/es/System.Linq.xml", - "ref/netstandard1.6/fr/System.Linq.xml", - "ref/netstandard1.6/it/System.Linq.xml", - "ref/netstandard1.6/ja/System.Linq.xml", - "ref/netstandard1.6/ko/System.Linq.xml", - "ref/netstandard1.6/ru/System.Linq.xml", - "ref/netstandard1.6/zh-hans/System.Linq.xml", - "ref/netstandard1.6/zh-hant/System.Linq.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.linq.4.3.0.nupkg.sha512", - "system.linq.nuspec" - ] - }, - "System.Linq.Expressions/4.3.0": { - "sha512": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "type": "package", - "path": "system.linq.expressions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Linq.Expressions.dll", - "lib/netcore50/System.Linq.Expressions.dll", - "lib/netstandard1.6/System.Linq.Expressions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Linq.Expressions.dll", - "ref/netcore50/System.Linq.Expressions.dll", - "ref/netcore50/System.Linq.Expressions.xml", - "ref/netcore50/de/System.Linq.Expressions.xml", - "ref/netcore50/es/System.Linq.Expressions.xml", - "ref/netcore50/fr/System.Linq.Expressions.xml", - "ref/netcore50/it/System.Linq.Expressions.xml", - "ref/netcore50/ja/System.Linq.Expressions.xml", - "ref/netcore50/ko/System.Linq.Expressions.xml", - "ref/netcore50/ru/System.Linq.Expressions.xml", - "ref/netcore50/zh-hans/System.Linq.Expressions.xml", - "ref/netcore50/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.0/System.Linq.Expressions.dll", - "ref/netstandard1.0/System.Linq.Expressions.xml", - "ref/netstandard1.0/de/System.Linq.Expressions.xml", - "ref/netstandard1.0/es/System.Linq.Expressions.xml", - "ref/netstandard1.0/fr/System.Linq.Expressions.xml", - "ref/netstandard1.0/it/System.Linq.Expressions.xml", - "ref/netstandard1.0/ja/System.Linq.Expressions.xml", - "ref/netstandard1.0/ko/System.Linq.Expressions.xml", - "ref/netstandard1.0/ru/System.Linq.Expressions.xml", - "ref/netstandard1.0/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.0/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.3/System.Linq.Expressions.dll", - "ref/netstandard1.3/System.Linq.Expressions.xml", - "ref/netstandard1.3/de/System.Linq.Expressions.xml", - "ref/netstandard1.3/es/System.Linq.Expressions.xml", - "ref/netstandard1.3/fr/System.Linq.Expressions.xml", - "ref/netstandard1.3/it/System.Linq.Expressions.xml", - "ref/netstandard1.3/ja/System.Linq.Expressions.xml", - "ref/netstandard1.3/ko/System.Linq.Expressions.xml", - "ref/netstandard1.3/ru/System.Linq.Expressions.xml", - "ref/netstandard1.3/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.3/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.6/System.Linq.Expressions.dll", - "ref/netstandard1.6/System.Linq.Expressions.xml", - "ref/netstandard1.6/de/System.Linq.Expressions.xml", - "ref/netstandard1.6/es/System.Linq.Expressions.xml", - "ref/netstandard1.6/fr/System.Linq.Expressions.xml", - "ref/netstandard1.6/it/System.Linq.Expressions.xml", - "ref/netstandard1.6/ja/System.Linq.Expressions.xml", - "ref/netstandard1.6/ko/System.Linq.Expressions.xml", - "ref/netstandard1.6/ru/System.Linq.Expressions.xml", - "ref/netstandard1.6/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.6/zh-hant/System.Linq.Expressions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Linq.Expressions.dll", - "system.linq.expressions.4.3.0.nupkg.sha512", - "system.linq.expressions.nuspec" - ] - }, - "System.Memory/4.5.4": { - "sha512": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", - "type": "package", - "path": "system.memory/4.5.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net461/System.Memory.dll", - "lib/net461/System.Memory.xml", - "lib/netcoreapp2.1/_._", - "lib/netstandard1.1/System.Memory.dll", - "lib/netstandard1.1/System.Memory.xml", - "lib/netstandard2.0/System.Memory.dll", - "lib/netstandard2.0/System.Memory.xml", - "ref/netcoreapp2.1/_._", - "system.memory.4.5.4.nupkg.sha512", - "system.memory.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Net.Http/4.3.4": { - "sha512": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", - "type": "package", - "path": "system.net.http/4.3.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/Xamarinmac20/_._", - "lib/monoandroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/net46/System.Net.Http.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/Xamarinmac20/_._", - "ref/monoandroid10/_._", - "ref/monotouch10/_._", - "ref/net45/_._", - "ref/net46/System.Net.Http.dll", - "ref/netcore50/System.Net.Http.dll", - "ref/netstandard1.1/System.Net.Http.dll", - "ref/netstandard1.3/System.Net.Http.dll", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll", - "runtimes/win/lib/net46/System.Net.Http.dll", - "runtimes/win/lib/netcore50/System.Net.Http.dll", - "runtimes/win/lib/netstandard1.3/System.Net.Http.dll", - "system.net.http.4.3.4.nupkg.sha512", - "system.net.http.nuspec" - ] - }, - "System.Net.Primitives/4.3.0": { - "sha512": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "type": "package", - "path": "system.net.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Net.Primitives.dll", - "ref/netcore50/System.Net.Primitives.xml", - "ref/netcore50/de/System.Net.Primitives.xml", - "ref/netcore50/es/System.Net.Primitives.xml", - "ref/netcore50/fr/System.Net.Primitives.xml", - "ref/netcore50/it/System.Net.Primitives.xml", - "ref/netcore50/ja/System.Net.Primitives.xml", - "ref/netcore50/ko/System.Net.Primitives.xml", - "ref/netcore50/ru/System.Net.Primitives.xml", - "ref/netcore50/zh-hans/System.Net.Primitives.xml", - "ref/netcore50/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.0/System.Net.Primitives.dll", - "ref/netstandard1.0/System.Net.Primitives.xml", - "ref/netstandard1.0/de/System.Net.Primitives.xml", - "ref/netstandard1.0/es/System.Net.Primitives.xml", - "ref/netstandard1.0/fr/System.Net.Primitives.xml", - "ref/netstandard1.0/it/System.Net.Primitives.xml", - "ref/netstandard1.0/ja/System.Net.Primitives.xml", - "ref/netstandard1.0/ko/System.Net.Primitives.xml", - "ref/netstandard1.0/ru/System.Net.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.1/System.Net.Primitives.dll", - "ref/netstandard1.1/System.Net.Primitives.xml", - "ref/netstandard1.1/de/System.Net.Primitives.xml", - "ref/netstandard1.1/es/System.Net.Primitives.xml", - "ref/netstandard1.1/fr/System.Net.Primitives.xml", - "ref/netstandard1.1/it/System.Net.Primitives.xml", - "ref/netstandard1.1/ja/System.Net.Primitives.xml", - "ref/netstandard1.1/ko/System.Net.Primitives.xml", - "ref/netstandard1.1/ru/System.Net.Primitives.xml", - "ref/netstandard1.1/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.1/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.3/System.Net.Primitives.dll", - "ref/netstandard1.3/System.Net.Primitives.xml", - "ref/netstandard1.3/de/System.Net.Primitives.xml", - "ref/netstandard1.3/es/System.Net.Primitives.xml", - "ref/netstandard1.3/fr/System.Net.Primitives.xml", - "ref/netstandard1.3/it/System.Net.Primitives.xml", - "ref/netstandard1.3/ja/System.Net.Primitives.xml", - "ref/netstandard1.3/ko/System.Net.Primitives.xml", - "ref/netstandard1.3/ru/System.Net.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.Net.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.net.primitives.4.3.0.nupkg.sha512", - "system.net.primitives.nuspec" - ] - }, - "System.Net.Sockets/4.3.0": { - "sha512": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", - "type": "package", - "path": "system.net.sockets/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.Sockets.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.Sockets.dll", - "ref/netstandard1.3/System.Net.Sockets.dll", - "ref/netstandard1.3/System.Net.Sockets.xml", - "ref/netstandard1.3/de/System.Net.Sockets.xml", - "ref/netstandard1.3/es/System.Net.Sockets.xml", - "ref/netstandard1.3/fr/System.Net.Sockets.xml", - "ref/netstandard1.3/it/System.Net.Sockets.xml", - "ref/netstandard1.3/ja/System.Net.Sockets.xml", - "ref/netstandard1.3/ko/System.Net.Sockets.xml", - "ref/netstandard1.3/ru/System.Net.Sockets.xml", - "ref/netstandard1.3/zh-hans/System.Net.Sockets.xml", - "ref/netstandard1.3/zh-hant/System.Net.Sockets.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.net.sockets.4.3.0.nupkg.sha512", - "system.net.sockets.nuspec" - ] - }, - "System.ObjectModel/4.3.0": { - "sha512": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "type": "package", - "path": "system.objectmodel/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.ObjectModel.dll", - "lib/netstandard1.3/System.ObjectModel.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.ObjectModel.dll", - "ref/netcore50/System.ObjectModel.xml", - "ref/netcore50/de/System.ObjectModel.xml", - "ref/netcore50/es/System.ObjectModel.xml", - "ref/netcore50/fr/System.ObjectModel.xml", - "ref/netcore50/it/System.ObjectModel.xml", - "ref/netcore50/ja/System.ObjectModel.xml", - "ref/netcore50/ko/System.ObjectModel.xml", - "ref/netcore50/ru/System.ObjectModel.xml", - "ref/netcore50/zh-hans/System.ObjectModel.xml", - "ref/netcore50/zh-hant/System.ObjectModel.xml", - "ref/netstandard1.0/System.ObjectModel.dll", - "ref/netstandard1.0/System.ObjectModel.xml", - "ref/netstandard1.0/de/System.ObjectModel.xml", - "ref/netstandard1.0/es/System.ObjectModel.xml", - "ref/netstandard1.0/fr/System.ObjectModel.xml", - "ref/netstandard1.0/it/System.ObjectModel.xml", - "ref/netstandard1.0/ja/System.ObjectModel.xml", - "ref/netstandard1.0/ko/System.ObjectModel.xml", - "ref/netstandard1.0/ru/System.ObjectModel.xml", - "ref/netstandard1.0/zh-hans/System.ObjectModel.xml", - "ref/netstandard1.0/zh-hant/System.ObjectModel.xml", - "ref/netstandard1.3/System.ObjectModel.dll", - "ref/netstandard1.3/System.ObjectModel.xml", - "ref/netstandard1.3/de/System.ObjectModel.xml", - "ref/netstandard1.3/es/System.ObjectModel.xml", - "ref/netstandard1.3/fr/System.ObjectModel.xml", - "ref/netstandard1.3/it/System.ObjectModel.xml", - "ref/netstandard1.3/ja/System.ObjectModel.xml", - "ref/netstandard1.3/ko/System.ObjectModel.xml", - "ref/netstandard1.3/ru/System.ObjectModel.xml", - "ref/netstandard1.3/zh-hans/System.ObjectModel.xml", - "ref/netstandard1.3/zh-hant/System.ObjectModel.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.objectmodel.4.3.0.nupkg.sha512", - "system.objectmodel.nuspec" - ] - }, - "System.Reflection/4.3.0": { - "sha512": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "type": "package", - "path": "system.reflection/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Reflection.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Reflection.dll", - "ref/netcore50/System.Reflection.dll", - "ref/netcore50/System.Reflection.xml", - "ref/netcore50/de/System.Reflection.xml", - "ref/netcore50/es/System.Reflection.xml", - "ref/netcore50/fr/System.Reflection.xml", - "ref/netcore50/it/System.Reflection.xml", - "ref/netcore50/ja/System.Reflection.xml", - "ref/netcore50/ko/System.Reflection.xml", - "ref/netcore50/ru/System.Reflection.xml", - "ref/netcore50/zh-hans/System.Reflection.xml", - "ref/netcore50/zh-hant/System.Reflection.xml", - "ref/netstandard1.0/System.Reflection.dll", - "ref/netstandard1.0/System.Reflection.xml", - "ref/netstandard1.0/de/System.Reflection.xml", - "ref/netstandard1.0/es/System.Reflection.xml", - "ref/netstandard1.0/fr/System.Reflection.xml", - "ref/netstandard1.0/it/System.Reflection.xml", - "ref/netstandard1.0/ja/System.Reflection.xml", - "ref/netstandard1.0/ko/System.Reflection.xml", - "ref/netstandard1.0/ru/System.Reflection.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.xml", - "ref/netstandard1.3/System.Reflection.dll", - "ref/netstandard1.3/System.Reflection.xml", - "ref/netstandard1.3/de/System.Reflection.xml", - "ref/netstandard1.3/es/System.Reflection.xml", - "ref/netstandard1.3/fr/System.Reflection.xml", - "ref/netstandard1.3/it/System.Reflection.xml", - "ref/netstandard1.3/ja/System.Reflection.xml", - "ref/netstandard1.3/ko/System.Reflection.xml", - "ref/netstandard1.3/ru/System.Reflection.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.xml", - "ref/netstandard1.5/System.Reflection.dll", - "ref/netstandard1.5/System.Reflection.xml", - "ref/netstandard1.5/de/System.Reflection.xml", - "ref/netstandard1.5/es/System.Reflection.xml", - "ref/netstandard1.5/fr/System.Reflection.xml", - "ref/netstandard1.5/it/System.Reflection.xml", - "ref/netstandard1.5/ja/System.Reflection.xml", - "ref/netstandard1.5/ko/System.Reflection.xml", - "ref/netstandard1.5/ru/System.Reflection.xml", - "ref/netstandard1.5/zh-hans/System.Reflection.xml", - "ref/netstandard1.5/zh-hant/System.Reflection.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.4.3.0.nupkg.sha512", - "system.reflection.nuspec" - ] - }, - "System.Reflection.Emit/4.3.0": { - "sha512": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "type": "package", - "path": "system.reflection.emit/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.dll", - "lib/netstandard1.3/System.Reflection.Emit.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/net45/_._", - "ref/netstandard1.1/System.Reflection.Emit.dll", - "ref/netstandard1.1/System.Reflection.Emit.xml", - "ref/netstandard1.1/de/System.Reflection.Emit.xml", - "ref/netstandard1.1/es/System.Reflection.Emit.xml", - "ref/netstandard1.1/fr/System.Reflection.Emit.xml", - "ref/netstandard1.1/it/System.Reflection.Emit.xml", - "ref/netstandard1.1/ja/System.Reflection.Emit.xml", - "ref/netstandard1.1/ko/System.Reflection.Emit.xml", - "ref/netstandard1.1/ru/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml", - "ref/xamarinmac20/_._", - "system.reflection.emit.4.3.0.nupkg.sha512", - "system.reflection.emit.nuspec" - ] - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "sha512": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "type": "package", - "path": "system.reflection.emit.ilgeneration/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.ILGeneration.dll", - "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll", - "lib/portable-net45+wp8/_._", - "lib/wp80/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.dll", - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/de/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/es/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/fr/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/it/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ja/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ko/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ru/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Emit.ILGeneration.xml", - "ref/portable-net45+wp8/_._", - "ref/wp80/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/_._", - "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512", - "system.reflection.emit.ilgeneration.nuspec" - ] - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "sha512": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "type": "package", - "path": "system.reflection.emit.lightweight/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.Lightweight.dll", - "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll", - "lib/portable-net45+wp8/_._", - "lib/wp80/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.dll", - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/de/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/es/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/fr/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/it/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ja/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ko/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ru/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Emit.Lightweight.xml", - "ref/portable-net45+wp8/_._", - "ref/wp80/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/_._", - "system.reflection.emit.lightweight.4.3.0.nupkg.sha512", - "system.reflection.emit.lightweight.nuspec" - ] - }, - "System.Reflection.Extensions/4.3.0": { - "sha512": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "type": "package", - "path": "system.reflection.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Reflection.Extensions.dll", - "ref/netcore50/System.Reflection.Extensions.xml", - "ref/netcore50/de/System.Reflection.Extensions.xml", - "ref/netcore50/es/System.Reflection.Extensions.xml", - "ref/netcore50/fr/System.Reflection.Extensions.xml", - "ref/netcore50/it/System.Reflection.Extensions.xml", - "ref/netcore50/ja/System.Reflection.Extensions.xml", - "ref/netcore50/ko/System.Reflection.Extensions.xml", - "ref/netcore50/ru/System.Reflection.Extensions.xml", - "ref/netcore50/zh-hans/System.Reflection.Extensions.xml", - "ref/netcore50/zh-hant/System.Reflection.Extensions.xml", - "ref/netstandard1.0/System.Reflection.Extensions.dll", - "ref/netstandard1.0/System.Reflection.Extensions.xml", - "ref/netstandard1.0/de/System.Reflection.Extensions.xml", - "ref/netstandard1.0/es/System.Reflection.Extensions.xml", - "ref/netstandard1.0/fr/System.Reflection.Extensions.xml", - "ref/netstandard1.0/it/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ja/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ko/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ru/System.Reflection.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.extensions.4.3.0.nupkg.sha512", - "system.reflection.extensions.nuspec" - ] - }, - "System.Reflection.Metadata/1.6.0": { - "sha512": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==", - "type": "package", - "path": "system.reflection.metadata/1.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.1/System.Reflection.Metadata.dll", - "lib/netstandard1.1/System.Reflection.Metadata.xml", - "lib/netstandard2.0/System.Reflection.Metadata.dll", - "lib/netstandard2.0/System.Reflection.Metadata.xml", - "lib/portable-net45+win8/System.Reflection.Metadata.dll", - "lib/portable-net45+win8/System.Reflection.Metadata.xml", - "system.reflection.metadata.1.6.0.nupkg.sha512", - "system.reflection.metadata.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Reflection.Primitives/4.3.0": { - "sha512": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "type": "package", - "path": "system.reflection.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Reflection.Primitives.dll", - "ref/netcore50/System.Reflection.Primitives.xml", - "ref/netcore50/de/System.Reflection.Primitives.xml", - "ref/netcore50/es/System.Reflection.Primitives.xml", - "ref/netcore50/fr/System.Reflection.Primitives.xml", - "ref/netcore50/it/System.Reflection.Primitives.xml", - "ref/netcore50/ja/System.Reflection.Primitives.xml", - "ref/netcore50/ko/System.Reflection.Primitives.xml", - "ref/netcore50/ru/System.Reflection.Primitives.xml", - "ref/netcore50/zh-hans/System.Reflection.Primitives.xml", - "ref/netcore50/zh-hant/System.Reflection.Primitives.xml", - "ref/netstandard1.0/System.Reflection.Primitives.dll", - "ref/netstandard1.0/System.Reflection.Primitives.xml", - "ref/netstandard1.0/de/System.Reflection.Primitives.xml", - "ref/netstandard1.0/es/System.Reflection.Primitives.xml", - "ref/netstandard1.0/fr/System.Reflection.Primitives.xml", - "ref/netstandard1.0/it/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ja/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ko/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ru/System.Reflection.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.primitives.4.3.0.nupkg.sha512", - "system.reflection.primitives.nuspec" - ] - }, - "System.Reflection.TypeExtensions/4.3.0": { - "sha512": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "type": "package", - "path": "system.reflection.typeextensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Reflection.TypeExtensions.dll", - "lib/net462/System.Reflection.TypeExtensions.dll", - "lib/netcore50/System.Reflection.TypeExtensions.dll", - "lib/netstandard1.5/System.Reflection.TypeExtensions.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Reflection.TypeExtensions.dll", - "ref/net462/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.3/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.3/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/de/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/es/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/fr/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/it/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ja/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ko/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ru/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.5/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/de/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/es/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/fr/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/it/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ja/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ko/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ru/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/zh-hans/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/zh-hant/System.Reflection.TypeExtensions.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Reflection.TypeExtensions.dll", - "system.reflection.typeextensions.4.3.0.nupkg.sha512", - "system.reflection.typeextensions.nuspec" - ] - }, - "System.Resources.ResourceManager/4.3.0": { - "sha512": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "type": "package", - "path": "system.resources.resourcemanager/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Resources.ResourceManager.dll", - "ref/netcore50/System.Resources.ResourceManager.xml", - "ref/netcore50/de/System.Resources.ResourceManager.xml", - "ref/netcore50/es/System.Resources.ResourceManager.xml", - "ref/netcore50/fr/System.Resources.ResourceManager.xml", - "ref/netcore50/it/System.Resources.ResourceManager.xml", - "ref/netcore50/ja/System.Resources.ResourceManager.xml", - "ref/netcore50/ko/System.Resources.ResourceManager.xml", - "ref/netcore50/ru/System.Resources.ResourceManager.xml", - "ref/netcore50/zh-hans/System.Resources.ResourceManager.xml", - "ref/netcore50/zh-hant/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/System.Resources.ResourceManager.dll", - "ref/netstandard1.0/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/de/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/es/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/fr/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/it/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ja/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ko/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ru/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/zh-hans/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/zh-hant/System.Resources.ResourceManager.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.resources.resourcemanager.4.3.0.nupkg.sha512", - "system.resources.resourcemanager.nuspec" - ] - }, - "System.Runtime/4.3.0": { - "sha512": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "type": "package", - "path": "system.runtime/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.dll", - "lib/portable-net45+win8+wp80+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.dll", - "ref/netcore50/System.Runtime.dll", - "ref/netcore50/System.Runtime.xml", - "ref/netcore50/de/System.Runtime.xml", - "ref/netcore50/es/System.Runtime.xml", - "ref/netcore50/fr/System.Runtime.xml", - "ref/netcore50/it/System.Runtime.xml", - "ref/netcore50/ja/System.Runtime.xml", - "ref/netcore50/ko/System.Runtime.xml", - "ref/netcore50/ru/System.Runtime.xml", - "ref/netcore50/zh-hans/System.Runtime.xml", - "ref/netcore50/zh-hant/System.Runtime.xml", - "ref/netstandard1.0/System.Runtime.dll", - "ref/netstandard1.0/System.Runtime.xml", - "ref/netstandard1.0/de/System.Runtime.xml", - "ref/netstandard1.0/es/System.Runtime.xml", - "ref/netstandard1.0/fr/System.Runtime.xml", - "ref/netstandard1.0/it/System.Runtime.xml", - "ref/netstandard1.0/ja/System.Runtime.xml", - "ref/netstandard1.0/ko/System.Runtime.xml", - "ref/netstandard1.0/ru/System.Runtime.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.xml", - "ref/netstandard1.2/System.Runtime.dll", - "ref/netstandard1.2/System.Runtime.xml", - "ref/netstandard1.2/de/System.Runtime.xml", - "ref/netstandard1.2/es/System.Runtime.xml", - "ref/netstandard1.2/fr/System.Runtime.xml", - "ref/netstandard1.2/it/System.Runtime.xml", - "ref/netstandard1.2/ja/System.Runtime.xml", - "ref/netstandard1.2/ko/System.Runtime.xml", - "ref/netstandard1.2/ru/System.Runtime.xml", - "ref/netstandard1.2/zh-hans/System.Runtime.xml", - "ref/netstandard1.2/zh-hant/System.Runtime.xml", - "ref/netstandard1.3/System.Runtime.dll", - "ref/netstandard1.3/System.Runtime.xml", - "ref/netstandard1.3/de/System.Runtime.xml", - "ref/netstandard1.3/es/System.Runtime.xml", - "ref/netstandard1.3/fr/System.Runtime.xml", - "ref/netstandard1.3/it/System.Runtime.xml", - "ref/netstandard1.3/ja/System.Runtime.xml", - "ref/netstandard1.3/ko/System.Runtime.xml", - "ref/netstandard1.3/ru/System.Runtime.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.xml", - "ref/netstandard1.5/System.Runtime.dll", - "ref/netstandard1.5/System.Runtime.xml", - "ref/netstandard1.5/de/System.Runtime.xml", - "ref/netstandard1.5/es/System.Runtime.xml", - "ref/netstandard1.5/fr/System.Runtime.xml", - "ref/netstandard1.5/it/System.Runtime.xml", - "ref/netstandard1.5/ja/System.Runtime.xml", - "ref/netstandard1.5/ko/System.Runtime.xml", - "ref/netstandard1.5/ru/System.Runtime.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.xml", - "ref/portable-net45+win8+wp80+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.4.3.0.nupkg.sha512", - "system.runtime.nuspec" - ] - }, - "System.Runtime.Extensions/4.3.0": { - "sha512": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "type": "package", - "path": "system.runtime.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.Extensions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.Extensions.dll", - "ref/netcore50/System.Runtime.Extensions.dll", - "ref/netcore50/System.Runtime.Extensions.xml", - "ref/netcore50/de/System.Runtime.Extensions.xml", - "ref/netcore50/es/System.Runtime.Extensions.xml", - "ref/netcore50/fr/System.Runtime.Extensions.xml", - "ref/netcore50/it/System.Runtime.Extensions.xml", - "ref/netcore50/ja/System.Runtime.Extensions.xml", - "ref/netcore50/ko/System.Runtime.Extensions.xml", - "ref/netcore50/ru/System.Runtime.Extensions.xml", - "ref/netcore50/zh-hans/System.Runtime.Extensions.xml", - "ref/netcore50/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.0/System.Runtime.Extensions.dll", - "ref/netstandard1.0/System.Runtime.Extensions.xml", - "ref/netstandard1.0/de/System.Runtime.Extensions.xml", - "ref/netstandard1.0/es/System.Runtime.Extensions.xml", - "ref/netstandard1.0/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.0/it/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.3/System.Runtime.Extensions.dll", - "ref/netstandard1.3/System.Runtime.Extensions.xml", - "ref/netstandard1.3/de/System.Runtime.Extensions.xml", - "ref/netstandard1.3/es/System.Runtime.Extensions.xml", - "ref/netstandard1.3/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.3/it/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.5/System.Runtime.Extensions.dll", - "ref/netstandard1.5/System.Runtime.Extensions.xml", - "ref/netstandard1.5/de/System.Runtime.Extensions.xml", - "ref/netstandard1.5/es/System.Runtime.Extensions.xml", - "ref/netstandard1.5/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.5/it/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.extensions.4.3.0.nupkg.sha512", - "system.runtime.extensions.nuspec" - ] - }, - "System.Runtime.Handles/4.3.0": { - "sha512": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "type": "package", - "path": "system.runtime.handles/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/_._", - "ref/netstandard1.3/System.Runtime.Handles.dll", - "ref/netstandard1.3/System.Runtime.Handles.xml", - "ref/netstandard1.3/de/System.Runtime.Handles.xml", - "ref/netstandard1.3/es/System.Runtime.Handles.xml", - "ref/netstandard1.3/fr/System.Runtime.Handles.xml", - "ref/netstandard1.3/it/System.Runtime.Handles.xml", - "ref/netstandard1.3/ja/System.Runtime.Handles.xml", - "ref/netstandard1.3/ko/System.Runtime.Handles.xml", - "ref/netstandard1.3/ru/System.Runtime.Handles.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Handles.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Handles.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.handles.4.3.0.nupkg.sha512", - "system.runtime.handles.nuspec" - ] - }, - "System.Runtime.InteropServices/4.3.0": { - "sha512": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "type": "package", - "path": "system.runtime.interopservices/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.InteropServices.dll", - "lib/net463/System.Runtime.InteropServices.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.InteropServices.dll", - "ref/net463/System.Runtime.InteropServices.dll", - "ref/netcore50/System.Runtime.InteropServices.dll", - "ref/netcore50/System.Runtime.InteropServices.xml", - "ref/netcore50/de/System.Runtime.InteropServices.xml", - "ref/netcore50/es/System.Runtime.InteropServices.xml", - "ref/netcore50/fr/System.Runtime.InteropServices.xml", - "ref/netcore50/it/System.Runtime.InteropServices.xml", - "ref/netcore50/ja/System.Runtime.InteropServices.xml", - "ref/netcore50/ko/System.Runtime.InteropServices.xml", - "ref/netcore50/ru/System.Runtime.InteropServices.xml", - "ref/netcore50/zh-hans/System.Runtime.InteropServices.xml", - "ref/netcore50/zh-hant/System.Runtime.InteropServices.xml", - "ref/netcoreapp1.1/System.Runtime.InteropServices.dll", - "ref/netstandard1.1/System.Runtime.InteropServices.dll", - "ref/netstandard1.1/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/System.Runtime.InteropServices.dll", - "ref/netstandard1.2/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/System.Runtime.InteropServices.dll", - "ref/netstandard1.3/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/System.Runtime.InteropServices.dll", - "ref/netstandard1.5/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.InteropServices.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.interopservices.4.3.0.nupkg.sha512", - "system.runtime.interopservices.nuspec" - ] - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "sha512": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "type": "package", - "path": "system.runtime.interopservices.runtimeinformation/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/win8/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/wpa81/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512", - "system.runtime.interopservices.runtimeinformation.nuspec" - ] - }, - "System.Runtime.Numerics/4.3.0": { - "sha512": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "type": "package", - "path": "system.runtime.numerics/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Runtime.Numerics.dll", - "lib/netstandard1.3/System.Runtime.Numerics.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Runtime.Numerics.dll", - "ref/netcore50/System.Runtime.Numerics.xml", - "ref/netcore50/de/System.Runtime.Numerics.xml", - "ref/netcore50/es/System.Runtime.Numerics.xml", - "ref/netcore50/fr/System.Runtime.Numerics.xml", - "ref/netcore50/it/System.Runtime.Numerics.xml", - "ref/netcore50/ja/System.Runtime.Numerics.xml", - "ref/netcore50/ko/System.Runtime.Numerics.xml", - "ref/netcore50/ru/System.Runtime.Numerics.xml", - "ref/netcore50/zh-hans/System.Runtime.Numerics.xml", - "ref/netcore50/zh-hant/System.Runtime.Numerics.xml", - "ref/netstandard1.1/System.Runtime.Numerics.dll", - "ref/netstandard1.1/System.Runtime.Numerics.xml", - "ref/netstandard1.1/de/System.Runtime.Numerics.xml", - "ref/netstandard1.1/es/System.Runtime.Numerics.xml", - "ref/netstandard1.1/fr/System.Runtime.Numerics.xml", - "ref/netstandard1.1/it/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ja/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ko/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ru/System.Runtime.Numerics.xml", - "ref/netstandard1.1/zh-hans/System.Runtime.Numerics.xml", - "ref/netstandard1.1/zh-hant/System.Runtime.Numerics.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.numerics.4.3.0.nupkg.sha512", - "system.runtime.numerics.nuspec" - ] - }, - "System.Security.Cryptography.Algorithms/4.3.0": { - "sha512": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "type": "package", - "path": "system.security.cryptography.algorithms/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Algorithms.dll", - "lib/net461/System.Security.Cryptography.Algorithms.dll", - "lib/net463/System.Security.Cryptography.Algorithms.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Algorithms.dll", - "ref/net461/System.Security.Cryptography.Algorithms.dll", - "ref/net463/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.3/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.4/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net463/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/netcore50/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "system.security.cryptography.algorithms.4.3.0.nupkg.sha512", - "system.security.cryptography.algorithms.nuspec" - ] - }, - "System.Security.Cryptography.Cng/4.3.0": { - "sha512": "03idZOqFlsKRL4W+LuCpJ6dBYDUWReug6lZjBa3uJWnk5sPCUXckocevTaUA8iT/MFSrY/2HXkOt753xQ/cf8g==", - "type": "package", - "path": "system.security.cryptography.cng/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/net46/System.Security.Cryptography.Cng.dll", - "lib/net461/System.Security.Cryptography.Cng.dll", - "lib/net463/System.Security.Cryptography.Cng.dll", - "ref/net46/System.Security.Cryptography.Cng.dll", - "ref/net461/System.Security.Cryptography.Cng.dll", - "ref/net463/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.3/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.4/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.6/System.Security.Cryptography.Cng.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net463/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netstandard1.4/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", - "system.security.cryptography.cng.4.3.0.nupkg.sha512", - "system.security.cryptography.cng.nuspec" - ] - }, - "System.Security.Cryptography.Csp/4.3.0": { - "sha512": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "type": "package", - "path": "system.security.cryptography.csp/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Csp.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Csp.dll", - "ref/netstandard1.3/System.Security.Cryptography.Csp.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Csp.dll", - "runtimes/win/lib/netcore50/_._", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", - "system.security.cryptography.csp.4.3.0.nupkg.sha512", - "system.security.cryptography.csp.nuspec" - ] - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "sha512": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "type": "package", - "path": "system.security.cryptography.encoding/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Encoding.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Encoding.dll", - "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "ref/netstandard1.3/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/de/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/es/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/fr/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/it/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ja/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ko/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ru/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/zh-hans/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/zh-hant/System.Security.Cryptography.Encoding.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Encoding.dll", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "system.security.cryptography.encoding.4.3.0.nupkg.sha512", - "system.security.cryptography.encoding.nuspec" - ] - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "sha512": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "type": "package", - "path": "system.security.cryptography.openssl/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "ref/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "system.security.cryptography.openssl.4.3.0.nupkg.sha512", - "system.security.cryptography.openssl.nuspec" - ] - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "sha512": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "type": "package", - "path": "system.security.cryptography.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Primitives.dll", - "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Primitives.dll", - "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.cryptography.primitives.4.3.0.nupkg.sha512", - "system.security.cryptography.primitives.nuspec" - ] - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "sha512": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "type": "package", - "path": "system.security.cryptography.x509certificates/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.X509Certificates.dll", - "lib/net461/System.Security.Cryptography.X509Certificates.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.X509Certificates.dll", - "ref/net461/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/de/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/es/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/fr/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/it/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ja/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ko/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ru/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/zh-hans/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/zh-hant/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/de/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/es/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/fr/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/it/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ja/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ko/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ru/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/zh-hans/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/zh-hant/System.Security.Cryptography.X509Certificates.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/netcore50/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", - "system.security.cryptography.x509certificates.4.3.0.nupkg.sha512", - "system.security.cryptography.x509certificates.nuspec" - ] - }, - "System.Text.Encoding/4.3.0": { - "sha512": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "type": "package", - "path": "system.text.encoding/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Text.Encoding.dll", - "ref/netcore50/System.Text.Encoding.xml", - "ref/netcore50/de/System.Text.Encoding.xml", - "ref/netcore50/es/System.Text.Encoding.xml", - "ref/netcore50/fr/System.Text.Encoding.xml", - "ref/netcore50/it/System.Text.Encoding.xml", - "ref/netcore50/ja/System.Text.Encoding.xml", - "ref/netcore50/ko/System.Text.Encoding.xml", - "ref/netcore50/ru/System.Text.Encoding.xml", - "ref/netcore50/zh-hans/System.Text.Encoding.xml", - "ref/netcore50/zh-hant/System.Text.Encoding.xml", - "ref/netstandard1.0/System.Text.Encoding.dll", - "ref/netstandard1.0/System.Text.Encoding.xml", - "ref/netstandard1.0/de/System.Text.Encoding.xml", - "ref/netstandard1.0/es/System.Text.Encoding.xml", - "ref/netstandard1.0/fr/System.Text.Encoding.xml", - "ref/netstandard1.0/it/System.Text.Encoding.xml", - "ref/netstandard1.0/ja/System.Text.Encoding.xml", - "ref/netstandard1.0/ko/System.Text.Encoding.xml", - "ref/netstandard1.0/ru/System.Text.Encoding.xml", - "ref/netstandard1.0/zh-hans/System.Text.Encoding.xml", - "ref/netstandard1.0/zh-hant/System.Text.Encoding.xml", - "ref/netstandard1.3/System.Text.Encoding.dll", - "ref/netstandard1.3/System.Text.Encoding.xml", - "ref/netstandard1.3/de/System.Text.Encoding.xml", - "ref/netstandard1.3/es/System.Text.Encoding.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.xml", - "ref/netstandard1.3/it/System.Text.Encoding.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.encoding.4.3.0.nupkg.sha512", - "system.text.encoding.nuspec" - ] - }, - "System.Text.Encoding.Extensions/4.3.0": { - "sha512": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "type": "package", - "path": "system.text.encoding.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Text.Encoding.Extensions.dll", - "ref/netcore50/System.Text.Encoding.Extensions.xml", - "ref/netcore50/de/System.Text.Encoding.Extensions.xml", - "ref/netcore50/es/System.Text.Encoding.Extensions.xml", - "ref/netcore50/fr/System.Text.Encoding.Extensions.xml", - "ref/netcore50/it/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ja/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ko/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ru/System.Text.Encoding.Extensions.xml", - "ref/netcore50/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netcore50/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/System.Text.Encoding.Extensions.dll", - "ref/netstandard1.0/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/de/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/es/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/fr/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/it/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ja/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ko/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ru/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/System.Text.Encoding.Extensions.dll", - "ref/netstandard1.3/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/de/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/es/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/it/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.encoding.extensions.4.3.0.nupkg.sha512", - "system.text.encoding.extensions.nuspec" - ] - }, - "System.Text.Encodings.Web/4.5.0": { - "sha512": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==", - "type": "package", - "path": "system.text.encodings.web/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Text.Encodings.Web.dll", - "lib/netstandard1.0/System.Text.Encodings.Web.xml", - "lib/netstandard2.0/System.Text.Encodings.Web.dll", - "lib/netstandard2.0/System.Text.Encodings.Web.xml", - "system.text.encodings.web.4.5.0.nupkg.sha512", - "system.text.encodings.web.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Text.RegularExpressions/4.3.0": { - "sha512": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "type": "package", - "path": "system.text.regularexpressions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Text.RegularExpressions.dll", - "lib/netcore50/System.Text.RegularExpressions.dll", - "lib/netstandard1.6/System.Text.RegularExpressions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Text.RegularExpressions.dll", - "ref/netcore50/System.Text.RegularExpressions.dll", - "ref/netcore50/System.Text.RegularExpressions.xml", - "ref/netcore50/de/System.Text.RegularExpressions.xml", - "ref/netcore50/es/System.Text.RegularExpressions.xml", - "ref/netcore50/fr/System.Text.RegularExpressions.xml", - "ref/netcore50/it/System.Text.RegularExpressions.xml", - "ref/netcore50/ja/System.Text.RegularExpressions.xml", - "ref/netcore50/ko/System.Text.RegularExpressions.xml", - "ref/netcore50/ru/System.Text.RegularExpressions.xml", - "ref/netcore50/zh-hans/System.Text.RegularExpressions.xml", - "ref/netcore50/zh-hant/System.Text.RegularExpressions.xml", - "ref/netcoreapp1.1/System.Text.RegularExpressions.dll", - "ref/netstandard1.0/System.Text.RegularExpressions.dll", - "ref/netstandard1.0/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/zh-hant/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/System.Text.RegularExpressions.dll", - "ref/netstandard1.3/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/zh-hant/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/System.Text.RegularExpressions.dll", - "ref/netstandard1.6/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/zh-hant/System.Text.RegularExpressions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.regularexpressions.4.3.0.nupkg.sha512", - "system.text.regularexpressions.nuspec" - ] - }, - "System.Threading/4.3.0": { - "sha512": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "type": "package", - "path": "system.threading/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Threading.dll", - "lib/netstandard1.3/System.Threading.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.dll", - "ref/netcore50/System.Threading.xml", - "ref/netcore50/de/System.Threading.xml", - "ref/netcore50/es/System.Threading.xml", - "ref/netcore50/fr/System.Threading.xml", - "ref/netcore50/it/System.Threading.xml", - "ref/netcore50/ja/System.Threading.xml", - "ref/netcore50/ko/System.Threading.xml", - "ref/netcore50/ru/System.Threading.xml", - "ref/netcore50/zh-hans/System.Threading.xml", - "ref/netcore50/zh-hant/System.Threading.xml", - "ref/netstandard1.0/System.Threading.dll", - "ref/netstandard1.0/System.Threading.xml", - "ref/netstandard1.0/de/System.Threading.xml", - "ref/netstandard1.0/es/System.Threading.xml", - "ref/netstandard1.0/fr/System.Threading.xml", - "ref/netstandard1.0/it/System.Threading.xml", - "ref/netstandard1.0/ja/System.Threading.xml", - "ref/netstandard1.0/ko/System.Threading.xml", - "ref/netstandard1.0/ru/System.Threading.xml", - "ref/netstandard1.0/zh-hans/System.Threading.xml", - "ref/netstandard1.0/zh-hant/System.Threading.xml", - "ref/netstandard1.3/System.Threading.dll", - "ref/netstandard1.3/System.Threading.xml", - "ref/netstandard1.3/de/System.Threading.xml", - "ref/netstandard1.3/es/System.Threading.xml", - "ref/netstandard1.3/fr/System.Threading.xml", - "ref/netstandard1.3/it/System.Threading.xml", - "ref/netstandard1.3/ja/System.Threading.xml", - "ref/netstandard1.3/ko/System.Threading.xml", - "ref/netstandard1.3/ru/System.Threading.xml", - "ref/netstandard1.3/zh-hans/System.Threading.xml", - "ref/netstandard1.3/zh-hant/System.Threading.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Threading.dll", - "system.threading.4.3.0.nupkg.sha512", - "system.threading.nuspec" - ] - }, - "System.Threading.Tasks/4.3.0": { - "sha512": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "type": "package", - "path": "system.threading.tasks/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.Tasks.dll", - "ref/netcore50/System.Threading.Tasks.xml", - "ref/netcore50/de/System.Threading.Tasks.xml", - "ref/netcore50/es/System.Threading.Tasks.xml", - "ref/netcore50/fr/System.Threading.Tasks.xml", - "ref/netcore50/it/System.Threading.Tasks.xml", - "ref/netcore50/ja/System.Threading.Tasks.xml", - "ref/netcore50/ko/System.Threading.Tasks.xml", - "ref/netcore50/ru/System.Threading.Tasks.xml", - "ref/netcore50/zh-hans/System.Threading.Tasks.xml", - "ref/netcore50/zh-hant/System.Threading.Tasks.xml", - "ref/netstandard1.0/System.Threading.Tasks.dll", - "ref/netstandard1.0/System.Threading.Tasks.xml", - "ref/netstandard1.0/de/System.Threading.Tasks.xml", - "ref/netstandard1.0/es/System.Threading.Tasks.xml", - "ref/netstandard1.0/fr/System.Threading.Tasks.xml", - "ref/netstandard1.0/it/System.Threading.Tasks.xml", - "ref/netstandard1.0/ja/System.Threading.Tasks.xml", - "ref/netstandard1.0/ko/System.Threading.Tasks.xml", - "ref/netstandard1.0/ru/System.Threading.Tasks.xml", - "ref/netstandard1.0/zh-hans/System.Threading.Tasks.xml", - "ref/netstandard1.0/zh-hant/System.Threading.Tasks.xml", - "ref/netstandard1.3/System.Threading.Tasks.dll", - "ref/netstandard1.3/System.Threading.Tasks.xml", - "ref/netstandard1.3/de/System.Threading.Tasks.xml", - "ref/netstandard1.3/es/System.Threading.Tasks.xml", - "ref/netstandard1.3/fr/System.Threading.Tasks.xml", - "ref/netstandard1.3/it/System.Threading.Tasks.xml", - "ref/netstandard1.3/ja/System.Threading.Tasks.xml", - "ref/netstandard1.3/ko/System.Threading.Tasks.xml", - "ref/netstandard1.3/ru/System.Threading.Tasks.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Tasks.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Tasks.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.tasks.4.3.0.nupkg.sha512", - "system.threading.tasks.nuspec" - ] - }, - "System.Threading.Tasks.Extensions/4.3.0": { - "sha512": "npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==", - "type": "package", - "path": "system.threading.tasks.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll", - "lib/netstandard1.0/System.Threading.Tasks.Extensions.xml", - "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.xml", - "system.threading.tasks.extensions.4.3.0.nupkg.sha512", - "system.threading.tasks.extensions.nuspec" - ] - }, - "System.Threading.Timer/4.3.0": { - "sha512": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", - "type": "package", - "path": "system.threading.timer/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net451/_._", - "lib/portable-net451+win81+wpa81/_._", - "lib/win81/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net451/_._", - "ref/netcore50/System.Threading.Timer.dll", - "ref/netcore50/System.Threading.Timer.xml", - "ref/netcore50/de/System.Threading.Timer.xml", - "ref/netcore50/es/System.Threading.Timer.xml", - "ref/netcore50/fr/System.Threading.Timer.xml", - "ref/netcore50/it/System.Threading.Timer.xml", - "ref/netcore50/ja/System.Threading.Timer.xml", - "ref/netcore50/ko/System.Threading.Timer.xml", - "ref/netcore50/ru/System.Threading.Timer.xml", - "ref/netcore50/zh-hans/System.Threading.Timer.xml", - "ref/netcore50/zh-hant/System.Threading.Timer.xml", - "ref/netstandard1.2/System.Threading.Timer.dll", - "ref/netstandard1.2/System.Threading.Timer.xml", - "ref/netstandard1.2/de/System.Threading.Timer.xml", - "ref/netstandard1.2/es/System.Threading.Timer.xml", - "ref/netstandard1.2/fr/System.Threading.Timer.xml", - "ref/netstandard1.2/it/System.Threading.Timer.xml", - "ref/netstandard1.2/ja/System.Threading.Timer.xml", - "ref/netstandard1.2/ko/System.Threading.Timer.xml", - "ref/netstandard1.2/ru/System.Threading.Timer.xml", - "ref/netstandard1.2/zh-hans/System.Threading.Timer.xml", - "ref/netstandard1.2/zh-hant/System.Threading.Timer.xml", - "ref/portable-net451+win81+wpa81/_._", - "ref/win81/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.timer.4.3.0.nupkg.sha512", - "system.threading.timer.nuspec" - ] - }, - "System.Xml.ReaderWriter/4.3.0": { - "sha512": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "type": "package", - "path": "system.xml.readerwriter/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Xml.ReaderWriter.dll", - "lib/netcore50/System.Xml.ReaderWriter.dll", - "lib/netstandard1.3/System.Xml.ReaderWriter.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Xml.ReaderWriter.dll", - "ref/netcore50/System.Xml.ReaderWriter.dll", - "ref/netcore50/System.Xml.ReaderWriter.xml", - "ref/netcore50/de/System.Xml.ReaderWriter.xml", - "ref/netcore50/es/System.Xml.ReaderWriter.xml", - "ref/netcore50/fr/System.Xml.ReaderWriter.xml", - "ref/netcore50/it/System.Xml.ReaderWriter.xml", - "ref/netcore50/ja/System.Xml.ReaderWriter.xml", - "ref/netcore50/ko/System.Xml.ReaderWriter.xml", - "ref/netcore50/ru/System.Xml.ReaderWriter.xml", - "ref/netcore50/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netcore50/zh-hant/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/System.Xml.ReaderWriter.dll", - "ref/netstandard1.0/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/de/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/es/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/fr/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/it/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ja/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ko/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ru/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/zh-hant/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/System.Xml.ReaderWriter.dll", - "ref/netstandard1.3/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/de/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/es/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/fr/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/it/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ja/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ko/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ru/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/zh-hant/System.Xml.ReaderWriter.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.readerwriter.4.3.0.nupkg.sha512", - "system.xml.readerwriter.nuspec" - ] - }, - "System.Xml.XDocument/4.3.0": { - "sha512": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "type": "package", - "path": "system.xml.xdocument/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Xml.XDocument.dll", - "lib/netstandard1.3/System.Xml.XDocument.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Xml.XDocument.dll", - "ref/netcore50/System.Xml.XDocument.xml", - "ref/netcore50/de/System.Xml.XDocument.xml", - "ref/netcore50/es/System.Xml.XDocument.xml", - "ref/netcore50/fr/System.Xml.XDocument.xml", - "ref/netcore50/it/System.Xml.XDocument.xml", - "ref/netcore50/ja/System.Xml.XDocument.xml", - "ref/netcore50/ko/System.Xml.XDocument.xml", - "ref/netcore50/ru/System.Xml.XDocument.xml", - "ref/netcore50/zh-hans/System.Xml.XDocument.xml", - "ref/netcore50/zh-hant/System.Xml.XDocument.xml", - "ref/netstandard1.0/System.Xml.XDocument.dll", - "ref/netstandard1.0/System.Xml.XDocument.xml", - "ref/netstandard1.0/de/System.Xml.XDocument.xml", - "ref/netstandard1.0/es/System.Xml.XDocument.xml", - "ref/netstandard1.0/fr/System.Xml.XDocument.xml", - "ref/netstandard1.0/it/System.Xml.XDocument.xml", - "ref/netstandard1.0/ja/System.Xml.XDocument.xml", - "ref/netstandard1.0/ko/System.Xml.XDocument.xml", - "ref/netstandard1.0/ru/System.Xml.XDocument.xml", - "ref/netstandard1.0/zh-hans/System.Xml.XDocument.xml", - "ref/netstandard1.0/zh-hant/System.Xml.XDocument.xml", - "ref/netstandard1.3/System.Xml.XDocument.dll", - "ref/netstandard1.3/System.Xml.XDocument.xml", - "ref/netstandard1.3/de/System.Xml.XDocument.xml", - "ref/netstandard1.3/es/System.Xml.XDocument.xml", - "ref/netstandard1.3/fr/System.Xml.XDocument.xml", - "ref/netstandard1.3/it/System.Xml.XDocument.xml", - "ref/netstandard1.3/ja/System.Xml.XDocument.xml", - "ref/netstandard1.3/ko/System.Xml.XDocument.xml", - "ref/netstandard1.3/ru/System.Xml.XDocument.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XDocument.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XDocument.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.xdocument.4.3.0.nupkg.sha512", - "system.xml.xdocument.nuspec" - ] - }, - "Yoti/3.6.0": { - "sha512": "qtZ5CPwzAZX344iFIQ7lA3zI686/KsE8YW9PL1ZDUzYaNrt6TrIEwA03sHEp4GoQSSdyDgCKmhk2X2XA09Pwtg==", - "type": "package", - "path": "yoti/3.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Yoti.png", - "lib/net452/Yoti.Auth.dll", - "lib/net462/Yoti.Auth.dll", - "lib/net472/Yoti.Auth.dll", - "lib/net48/Yoti.Auth.dll", - "lib/netcoreapp1.1/Yoti.Auth.dll", - "lib/netcoreapp2.2/Yoti.Auth.dll", - "lib/netcoreapp3.1/Yoti.Auth.dll", - "lib/netstandard1.6/Yoti.Auth.dll", - "lib/netstandard2.1/Yoti.Auth.dll", - "yoti.3.6.0.nupkg.sha512", - "yoti.nuspec" - ] - } - }, - "projectFileDependencyGroups": { - ".NETCoreApp,Version=v3.1": [ - "DotNetEnv >= 1.4.0", - "Microsoft.AspNetCore.Hosting >= 2.2.7", - "Microsoft.Extensions.DependencyInjection >= 3.1.8", - "Microsoft.Extensions.Logging >= 3.1.8", - "Microsoft.Extensions.Logging.Console >= 3.1.8", - "Yoti >= 3.6.0", - "dotenv.net >= 2.1.1" - ] - }, - "packageFolders": { - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", - "projectName": "CoreExample", - "projectPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", - "packagesPath": "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages", - "outputPath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/obj/", - "projectStyle": "PackageReference", - "configFilePaths": [ - "/home/runner/.nuget/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "netcoreapp3.1" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp3.1": { - "targetAlias": "netcoreapp3.1", - "projectReferences": {} - } - }, - "warningProperties": { - "warnAsError": [ - "NU1605" - ] - }, - "restoreAuditProperties": { - "enableAudit": "true", - "auditLevel": "low", - "auditMode": "direct" - }, - "SdkAnalysisLevel": "10.0.100" - }, - "frameworks": { - "netcoreapp3.1": { - "targetAlias": "netcoreapp3.1", - "dependencies": { - "DotNetEnv": { - "target": "Package", - "version": "[1.4.0, )" - }, - "Microsoft.AspNetCore.Hosting": { - "target": "Package", - "version": "[2.2.7, )" - }, - "Microsoft.Extensions.DependencyInjection": { - "target": "Package", - "version": "[3.1.8, )" - }, - "Microsoft.Extensions.Logging": { - "target": "Package", - "version": "[3.1.8, )" - }, - "Microsoft.Extensions.Logging.Console": { - "target": "Package", - "version": "[3.1.8, )" - }, - "Yoti": { - "target": "Package", - "version": "[3.6.0, )" - }, - "dotenv.net": { - "target": "Package", - "version": "[2.1.1, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "downloadDependencies": [ - { - "name": "Microsoft.AspNetCore.App.Ref", - "version": "[3.1.10, 3.1.10]" - }, - { - "name": "Microsoft.NETCore.App.Host.linux-x64", - "version": "[3.1.32, 3.1.32]" - }, - { - "name": "Microsoft.NETCore.App.Ref", - "version": "[3.1.0, 3.1.0]" - } - ], - "frameworkReferences": { - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/10.0.100/RuntimeIdentifierGraph.json" - } - } - } -} \ No newline at end of file diff --git a/dotnet/CoreExample/CoreExample/obj/project.nuget.cache b/dotnet/CoreExample/CoreExample/obj/project.nuget.cache deleted file mode 100644 index 3e1c616..0000000 --- a/dotnet/CoreExample/CoreExample/obj/project.nuget.cache +++ /dev/null @@ -1,124 +0,0 @@ -{ - "version": 2, - "dgSpecHash": "My0XGmEiEkQ=", - "success": true, - "projectFilePath": "/home/runner/work/age-scan-examples/age-scan-examples/dotnet/CoreExample/CoreExample/CoreExample.csproj", - "expectedPackageFiles": [ - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/dotenv.net/2.1.1/dotenv.net.2.1.1.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/dotnetenv/1.4.0/dotnetenv.1.4.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/google.protobuf/3.12.3/google.protobuf.3.12.3.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/jsonsubtypes/1.7.0/jsonsubtypes.1.7.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.hosting/2.2.7/microsoft.aspnetcore.hosting.2.2.7.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.hosting.abstractions/2.2.0/microsoft.aspnetcore.hosting.abstractions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.hosting.server.abstractions/2.2.0/microsoft.aspnetcore.hosting.server.abstractions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http/2.2.0/microsoft.aspnetcore.http.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http.abstractions/2.2.0/microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http.extensions/2.2.0/microsoft.aspnetcore.http.extensions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.http.features/2.2.0/microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.webutilities/2.2.0/microsoft.aspnetcore.webutilities.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration/3.1.8/microsoft.extensions.configuration.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.abstractions/3.1.8/microsoft.extensions.configuration.abstractions.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.binder/3.1.8/microsoft.extensions.configuration.binder.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.environmentvariables/2.2.4/microsoft.extensions.configuration.environmentvariables.2.2.4.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.configuration.fileextensions/2.2.0/microsoft.extensions.configuration.fileextensions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.dependencyinjection/3.1.8/microsoft.extensions.dependencyinjection.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.dependencyinjection.abstractions/3.1.8/microsoft.extensions.dependencyinjection.abstractions.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.fileproviders.abstractions/2.2.0/microsoft.extensions.fileproviders.abstractions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.fileproviders.physical/2.2.0/microsoft.extensions.fileproviders.physical.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.filesystemglobbing/2.2.0/microsoft.extensions.filesystemglobbing.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.hosting.abstractions/2.2.0/microsoft.extensions.hosting.abstractions.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging/3.1.8/microsoft.extensions.logging.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging.abstractions/3.1.8/microsoft.extensions.logging.abstractions.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging.configuration/3.1.8/microsoft.extensions.logging.configuration.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.logging.console/3.1.8/microsoft.extensions.logging.console.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.objectpool/2.2.0/microsoft.extensions.objectpool.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.options/3.1.8/microsoft.extensions.options.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.options.configurationextensions/3.1.8/microsoft.extensions.options.configurationextensions.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.extensions.primitives/3.1.8/microsoft.extensions.primitives.3.1.8.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.net.http.headers/2.2.0/microsoft.net.http.headers.2.2.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.platforms/1.1.1/microsoft.netcore.platforms.1.1.1.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.targets/1.1.0/microsoft.netcore.targets.1.1.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.win32.primitives/4.3.0/microsoft.win32.primitives.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/netstandard.library/1.6.1/netstandard.library.1.6.1.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/newtonsoft.json/12.0.3/newtonsoft.json.12.0.3.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/nlog/4.7.2/nlog.4.7.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/portable.bouncycastle/1.8.5/portable.bouncycastle.1.8.5.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system/4.3.0/runtime.native.system.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.io.compression/4.3.0/runtime.native.system.io.compression.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.net.http/4.3.0/runtime.native.system.net.http.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.security.cryptography.apple/4.3.0/runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.native.system.security.cryptography.openssl/4.3.2/runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2/runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.appcontext/4.3.0/system.appcontext.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.buffers/4.5.0/system.buffers.4.5.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.collections/4.3.0/system.collections.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.collections.concurrent/4.3.0/system.collections.concurrent.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.console/4.3.0/system.console.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.debug/4.3.0/system.diagnostics.debug.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.diagnosticsource/4.5.1/system.diagnostics.diagnosticsource.4.5.1.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.tools/4.3.0/system.diagnostics.tools.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.diagnostics.tracing/4.3.0/system.diagnostics.tracing.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.globalization/4.3.0/system.globalization.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.globalization.calendars/4.3.0/system.globalization.calendars.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.globalization.extensions/4.3.0/system.globalization.extensions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io/4.3.0/system.io.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.compression/4.3.0/system.io.compression.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.compression.zipfile/4.3.0/system.io.compression.zipfile.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.filesystem/4.3.0/system.io.filesystem.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.io.filesystem.primitives/4.3.0/system.io.filesystem.primitives.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.linq/4.3.0/system.linq.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.linq.expressions/4.3.0/system.linq.expressions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.memory/4.5.4/system.memory.4.5.4.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.net.http/4.3.4/system.net.http.4.3.4.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.net.primitives/4.3.0/system.net.primitives.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.net.sockets/4.3.0/system.net.sockets.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.objectmodel/4.3.0/system.objectmodel.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection/4.3.0/system.reflection.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.emit/4.3.0/system.reflection.emit.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.emit.ilgeneration/4.3.0/system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.emit.lightweight/4.3.0/system.reflection.emit.lightweight.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.extensions/4.3.0/system.reflection.extensions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.metadata/1.6.0/system.reflection.metadata.1.6.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.primitives/4.3.0/system.reflection.primitives.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.reflection.typeextensions/4.3.0/system.reflection.typeextensions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.resources.resourcemanager/4.3.0/system.resources.resourcemanager.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime/4.3.0/system.runtime.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.extensions/4.3.0/system.runtime.extensions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.handles/4.3.0/system.runtime.handles.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.interopservices/4.3.0/system.runtime.interopservices.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.interopservices.runtimeinformation/4.3.0/system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.runtime.numerics/4.3.0/system.runtime.numerics.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.algorithms/4.3.0/system.security.cryptography.algorithms.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.cng/4.3.0/system.security.cryptography.cng.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.csp/4.3.0/system.security.cryptography.csp.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.encoding/4.3.0/system.security.cryptography.encoding.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.openssl/4.3.0/system.security.cryptography.openssl.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.primitives/4.3.0/system.security.cryptography.primitives.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.security.cryptography.x509certificates/4.3.0/system.security.cryptography.x509certificates.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.encoding/4.3.0/system.text.encoding.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.encoding.extensions/4.3.0/system.text.encoding.extensions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.encodings.web/4.5.0/system.text.encodings.web.4.5.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.text.regularexpressions/4.3.0/system.text.regularexpressions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading/4.3.0/system.threading.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading.tasks/4.3.0/system.threading.tasks.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading.tasks.extensions/4.3.0/system.threading.tasks.extensions.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.threading.timer/4.3.0/system.threading.timer.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.xml.readerwriter/4.3.0/system.xml.readerwriter.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/system.xml.xdocument/4.3.0/system.xml.xdocument.4.3.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/yoti/3.6.0/yoti.3.6.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.app.ref/3.1.0/microsoft.netcore.app.ref.3.1.0.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.aspnetcore.app.ref/3.1.10/microsoft.aspnetcore.app.ref.3.1.10.nupkg.sha512", - "/home/runner/work/age-scan-examples/.codeql-scratch/dbs/csharp/working/packages/microsoft.netcore.app.host.linux-x64/3.1.32/microsoft.netcore.app.host.linux-x64.3.1.32.nupkg.sha512" - ], - "logs": [] -} \ No newline at end of file From 3ddc222997e2f18ad835f5322bc2edb4bda1c27e Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:24:41 +0000 Subject: [PATCH 10/18] change data to img for payload --- dotnet/CoreExample/CoreExample/Program.cs | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dotnet/CoreExample/CoreExample/Program.cs b/dotnet/CoreExample/CoreExample/Program.cs index a87b025..ab35931 100644 --- a/dotnet/CoreExample/CoreExample/Program.cs +++ b/dotnet/CoreExample/CoreExample/Program.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Yoti.Auth.Web; using System; @@ -8,13 +8,13 @@ using Org.BouncyCastle.Crypto; using System.Text; -namespace CoreExample -{ - - class Program - { - static void Main(string[] args) - { +namespace CoreExample +{ + + class Program + { + static void Main(string[] args) + { var serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); var serviceProvider = serviceCollection.BuildServiceProvider(); @@ -46,7 +46,7 @@ static void Main(string[] args) string serializedRequest = Newtonsoft.Json.JsonConvert.SerializeObject(new { - data = Convert.ToBase64String(imgBytes) + img = Convert.ToBase64String(imgBytes) }); byte[] byteContent = Encoding.UTF8.GetBytes(serializedRequest); @@ -78,9 +78,9 @@ private static void ConfigureServices(IServiceCollection services) } - - } -} - - - + + } +} + + + From 3a92f8c0e46bda52319f07f60c304a4e17f58d67 Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:25:11 +0000 Subject: [PATCH 11/18] change data to img for payload --- go/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/main.go b/go/main.go index 63c91a2..5803ae9 100644 --- a/go/main.go +++ b/go/main.go @@ -15,7 +15,7 @@ import ( ) type Estimation struct { - Data string `json:"data"` + Img string `json:"img"` } func main(){ @@ -41,7 +41,7 @@ func main(){ encoded := base64.StdEncoding.EncodeToString(content) estimation := &Estimation{ - Data: encoded, + Img: encoded, } jsonData, err := json.Marshal(estimation) From c7f021ee6ce3e59b11514ed289abdd712daea0e8 Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:25:38 +0000 Subject: [PATCH 12/18] add go.mod and go.sum files --- go/go.mod | 8 ++++++++ go/go.sum | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 go/go.mod create mode 100644 go/go.sum diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 0000000..4b1eafb --- /dev/null +++ b/go/go.mod @@ -0,0 +1,8 @@ +module github.com/getyoti/age-scan-examples/go + +go 1.24.4 + +require ( + github.com/getyoti/yoti-go-sdk/v3 v3.15.0 + github.com/joho/godotenv v1.5.1 +) diff --git a/go/go.sum b/go/go.sum new file mode 100644 index 0000000..be30284 --- /dev/null +++ b/go/go.sum @@ -0,0 +1,8 @@ +github.com/getyoti/yoti-go-sdk/v3 v3.15.0 h1:jLbL6gGvNXUYaI8GHQ3S7uLoPk/S6SMeWaHI4TtSIFo= +github.com/getyoti/yoti-go-sdk/v3 v3.15.0/go.mod h1:FH8g7mRttc6SBUd9P0Jihm7ut0rNhkU3rDFljUHL33I= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= +gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= From 457843e4299a2059d219088648cec3a4aacd5e4b Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:25:56 +0000 Subject: [PATCH 13/18] change data to img for payload --- javascript/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/index.js b/javascript/index.js index 14ff857..6e99b25 100644 --- a/javascript/index.js +++ b/javascript/index.js @@ -4,7 +4,7 @@ const { RequestBuilder, Payload } = require('yoti'); const fs = require('fs') var image = fs.readFileSync(process.env.TEST_IMAGE_PATH); -var imageRequest = {"data": image.toString('base64')} +var imageRequest = {"img": image.toString('base64')} const request = new RequestBuilder() .withBaseUrl(process.env.BASE_URL) From e2870afbe3b53bcb89cf158bf75eb438924237d3 Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:26:16 +0000 Subject: [PATCH 14/18] change data to img for payload --- php/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/index.php b/php/index.php index 0cd7272..44fedae 100644 --- a/php/index.php +++ b/php/index.php @@ -21,7 +21,7 @@ die("Error: Could not read image file: $imagePath\n"); } -$payload = [ "data" => base64_encode($image) ]; +$payload = [ "img" => base64_encode($image) ]; $request = (new RequestBuilder()) ->withBaseUrl($baseUrl) From a10a7580457a9a81e11a5d0c542bf27b94a51aa2 Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:26:44 +0000 Subject: [PATCH 15/18] change data to img for payload --- python/example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/example.py b/python/example.py index 738e60a..5cbb310 100644 --- a/python/example.py +++ b/python/example.py @@ -17,7 +17,7 @@ def generate_session(): with open(os.getenv('TEST_IMAGE_PATH'), "rb") as image_file: encoded_string = base64.b64encode(image_file.read()) - data = {"data" : encoded_string.decode("utf-8")} + data = {"img" : encoded_string.decode("utf-8")} payload_string = json.dumps(data).encode() From 63b0beb568eb194741fafd09bcca1ffc1a36fbe0 Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:28:04 +0000 Subject: [PATCH 16/18] update README to specify where to add PEM file --- java/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/README.md b/java/README.md index 797bec3..6431636 100644 --- a/java/README.md +++ b/java/README.md @@ -1,6 +1,6 @@ # Java Example -- Save your PEM file into the keys directory and name it key.pem +- Save your PEM file into the `resources/keys` directory and name it key.pem - Edit the `application.properties` file, adding client SDK ID - Optional - Update the ENDPOINT in `application.properties` (default is `age-antispoofing`, can be `age`, `antispoofing`, or `age-antispoofing`) - Optional - Replace the testimage.jpg with your own. From ee6de0a4a13d1c96e927a32263387cd136d7719b Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:28:35 +0000 Subject: [PATCH 17/18] update pom.xml --- java/pom.xml | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index e7b1c6f..b2e3ac2 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.yoti @@ -18,6 +18,45 @@ + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + true + com.yoti.agescan.Application + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + org.apache.maven.plugins maven-deploy-plugin @@ -46,4 +85,4 @@ - + \ No newline at end of file From d4f7caf6870eeb6e35d59a5720655612a9f6343e Mon Sep 17 00:00:00 2001 From: Nikhil Pankhania Date: Thu, 27 Nov 2025 12:33:52 +0000 Subject: [PATCH 18/18] switch PEM key and image file loading and update payload field name from data to img --- .../java/com/yoti/agescan/Application.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/java/src/main/java/com/yoti/agescan/Application.java b/java/src/main/java/com/yoti/agescan/Application.java index 3a46ec0..6db9d40 100644 --- a/java/src/main/java/com/yoti/agescan/Application.java +++ b/java/src/main/java/com/yoti/agescan/Application.java @@ -35,18 +35,22 @@ public static void main(String[] args) { BufferedImage bufferedImage = null; ByteArrayOutputStream byteArrayOutputStream = null; try { - bufferedImage = ImageIO.read(new File(Application.class.getClassLoader(). - getResource(prop.getProperty("TEST_IMAGE_PATH")). - getFile())); + InputStream imageStream = Application.class.getClassLoader() + .getResourceAsStream(prop.getProperty("TEST_IMAGE_PATH")); + if (imageStream == null) { + System.out.println("Image not found in resources!"); + return; + } + bufferedImage = ImageIO.read(imageStream); byteArrayOutputStream = new ByteArrayOutputStream(); - ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream ); + ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream); } catch (IOException e) { e.printStackTrace(); } byte[] image = byteArrayOutputStream.toByteArray(); JsonObject body = new JsonObject(); - body.put("data", image); + body.put("img", image); byte[] payload = body.encode().getBytes(); @@ -74,8 +78,11 @@ public static void main(String[] args) { * @throws IOException */ private static KeyPair findKeyPair() throws IOException { - InputStream keyStream = new FileInputStream(Application.class.getClassLoader().getResource(prop.getProperty("PEM_FILE_PATH")).getFile()); - PEMParser reader = new PEMParser(new BufferedReader(new InputStreamReader(keyStream, Charset.defaultCharset()))); + InputStream keyStream = Application.class.getClassLoader() + .getResourceAsStream(prop.getProperty("PEM_FILE_PATH")); + if (keyStream == null) { + throw new FileNotFoundException("PEM key file not found in resources!"); + } PEMParser reader = new PEMParser(new BufferedReader(new InputStreamReader(keyStream, Charset.defaultCharset()))); KeyPair keyPair = null; for (Object o = null; (o = reader.readObject()) != null;) { if (o instanceof PEMKeyPair) {