Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Migration of .io to .com + sync/async/webhook methods #2

Open
wants to merge 138 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
c3cb8fb
feat(repo): Update instances of .io to .com
AJCJ1 Oct 14, 2024
5b94c0c
feat(test): update tests to use compatible .net version for macOS
AJCJ1 Oct 14, 2024
f66ce29
fix(repo): prepare repo for nuget package upload
AJCJ1 Oct 15, 2024
b09d611
fix(repo): clarify code comments
AJCJ1 Oct 15, 2024
0d80539
feat(repo): add docbblocks to methods
AJCJ1 Oct 15, 2024
0c23595
feat(repo): Use class input UrlboxOptions to improve typing
AJCJ1 Oct 16, 2024
aaa9c63
feat(repo): Upgrade package from dotnet 2.0 -> 6.0
AJCJ1 Oct 16, 2024
d3bffb0
feat(repo): Install dependencies for using env vars locally and in gh…
AJCJ1 Oct 16, 2024
fbc94b0
feat(repo): add webhook secret to Urlbox overload, and urlbox api end…
AJCJ1 Oct 16, 2024
33eb90b
feat(repo): implement post sync and post async methods, including res…
AJCJ1 Oct 16, 2024
5fa8daf
feat(repo): add tests for render and render async endpoints for succe…
AJCJ1 Oct 16, 2024
1bf6b38
fix(repo): fix blockUrls to be arr
AJCJ1 Oct 17, 2024
f830362
fix(repo): ensure strings are included in array string representations
AJCJ1 Oct 17, 2024
cd3999e
fix(repo): change cookies to obj and fix string interpretation of array
AJCJ1 Oct 17, 2024
7fd9e3a
fix(repo): update cookie type to be tighter to string OR string[]
AJCJ1 Oct 17, 2024
8bd1ccf
refactor(repo): update urlboxoptions types
AJCJ1 Oct 17, 2024
1ad7137
refactor(repo): split classes into their own files
AJCJ1 Oct 17, 2024
1775496
feat(repo): add a fromCredentials static method, and a webhook verifi…
AJCJ1 Oct 17, 2024
271f38c
refactor(repo): make global usings and organise resources and policie…
AJCJ1 Oct 18, 2024
230cdc3
fix(repo): update validator to handle header edge cases
AJCJ1 Oct 18, 2024
fa2c6be
feat(repo): add contribution guide including how to set env vars for …
AJCJ1 Oct 18, 2024
2bed5f8
refactor(readme): update readme with new usage examples including new…
AJCJ1 Oct 18, 2024
dc9122c
refactor(repo): update readme with static fromcredentials example, fi…
AJCJ1 Oct 18, 2024
ba82c5c
fix(repo): ensure options only accepts one of html or url but not both
AJCJ1 Oct 18, 2024
7b812a1
refactor(repo): move urlboxoptions tests to own file
AJCJ1 Oct 18, 2024
79683d0
feat(repo): add metadata/side renders as types to sync response, with…
AJCJ1 Oct 22, 2024
d4849b2
fix(repo): make download method throw error with urlbox error message
AJCJ1 Oct 22, 2024
5f7f8c4
fix(repo): update urlbox sln to recognise the urlbox.csproj file
AJCJ1 Oct 22, 2024
654e815
chore(repo): remove unused imports
AJCJ1 Oct 29, 2024
de91184
add test to urlboxoptions
AJCJ1 Oct 29, 2024
cab5add
fix(repo): seal urlbox class
AJCJ1 Oct 29, 2024
75bcd8f
make urlbox values readonly private, remove unused webhookSecret whic…
AJCJ1 Oct 29, 2024
566d7c3
Implement getUrlboxErrorMessage, which extracts from err message in h…
AJCJ1 Oct 29, 2024
9932552
chore(tests): cleanup tests
AJCJ1 Oct 29, 2024
50e8726
chore(repo): place classes into file namespaces
AJCJ1 Oct 29, 2024
4eb2c3e
refactor(repo): implement abstract urlbox response
AJCJ1 Oct 29, 2024
336a1d4
feat(repo): implement takeScreenshot
AJCJ1 Oct 29, 2024
e0f6739
chore(repo): capitalise verifywebhooksignature method
AJCJ1 Oct 29, 2024
bcfd538
chore(repo): reorder methods into categories with public/private
AJCJ1 Oct 29, 2024
9b7cb48
feat(repo): create Urlbox interface + factory for Dependency injection
AJCJ1 Oct 29, 2024
2a62a34
feat(tests): Add tests for checking Urlbox can be instantiated via DI…
AJCJ1 Oct 29, 2024
86e3f97
feat(tests): enable parallel testing
AJCJ1 Oct 29, 2024
73a3be0
feat(repo): add helper methods takemp4 takepdf takewithmeta takemobil…
AJCJ1 Nov 5, 2024
0b55577
feat(repo): update the readme WIP
AJCJ1 Nov 5, 2024
8861080
feat(repo): update readme
AJCJ1 Nov 8, 2024
12bcedd
feat(repo): add section on render links and the api reference in readme:
AJCJ1 Nov 8, 2024
bca79c1
fix(repo): add webhookUrl to tests
AJCJ1 Nov 8, 2024
46bd6d4
chore(repo): add note on DI in readme
AJCJ1 Nov 8, 2024
e1f6b40
refactor(repo): verifyWebhookSignature returns the full reponse objec…
AJCJ1 Nov 11, 2024
8709d7a
chore(repo): move resources into their own dirs
AJCJ1 Nov 14, 2024
2d2beec
feat(repo): create an options builder for fluent options creation, wi…
AJCJ1 Nov 14, 2024
8fa6d82
feat(repo): add in tests for validating screenshot options
AJCJ1 Nov 14, 2024
4a330ab
feat(repo): implement validation for engine version
AJCJ1 Nov 18, 2024
c17b3fd
refactor(repo): update readme to include usage of the urlbox options …
AJCJ1 Nov 18, 2024
3481c90
feat(repo): upgrade package version to 2.0.0, add changelog to readme
AJCJ1 Nov 18, 2024
7099e09
chore(repo): add changelog t TOC
AJCJ1 Nov 18, 2024
272c7a7
refactor(repo): proof read repo
AJCJ1 Nov 18, 2024
a10306d
fix(repo): add gh image not showing in repo variant of readme
AJCJ1 Nov 18, 2024
ffb8d88
chore(repo): remove old xunit tests
AJCJ1 Nov 21, 2024
82cc0a8
fix(readme): replace occurrences of screenshotting with rendering
AJCJ1 Nov 25, 2024
ea4a2b7
fix(readme): add explanation for getting project keys with screenshot
AJCJ1 Nov 25, 2024
40e40df
fix(repo): rename namespace from screenshots to UrlboxSDK
AJCJ1 Nov 25, 2024
97af568
chore(repo): rename GenerateUrlboxUrl() to GenerateRenderLink()
AJCJ1 Nov 25, 2024
169061a
fix(readme): update comments in JSON to use #
AJCJ1 Nov 25, 2024
92252c2
chore(repo): rename GenerateUrlboxUrl() to GenerateRenderLink()
AJCJ1 Nov 25, 2024
1fb01fb
fix(readme): update s3 comment, replace lowercased .render with .Render
AJCJ1 Nov 25, 2024
c5ab956
feat(repo): add signed and unsigned variants of render link calls
AJCJ1 Nov 25, 2024
f9c0e2e
refactor(repo): GetStatus() now takes a render ID instead of the full…
AJCJ1 Nov 25, 2024
bbe9bd9
fix(repo): update snakeCaseNamingPolicy with tests
AJCJ1 Nov 25, 2024
4a9b3c4
feat(repo): introduce UrlboxException, thrown on err with more meanin…
AJCJ1 Nov 25, 2024
12d4348
fix(repo): update urlboxexception to not use init
AJCJ1 Nov 26, 2024
1f59cec
fix(repo): wip - enable nullable on project, introduce enums, tests p…
AJCJ1 Nov 26, 2024
5793aba
chore(repo): organise files into directory structure
AJCJ1 Nov 27, 2024
35cdb89
fix(repo): handle nullable warnings that came out from enabling nulla…
AJCJ1 Nov 27, 2024
d55ea10
feat(repo): add final enum types to options
AJCJ1 Nov 29, 2024
7b901e8
chore(repo): cleanup use of this. and reserved keywords
AJCJ1 Nov 29, 2024
f36c489
fix(repo): make ints nullable in options
AJCJ1 Nov 29, 2024
0ee9494
feat(repo): add ability to override base url, with tests
AJCJ1 Nov 29, 2024
f043db0
feat(repo): add ability to use Render() and RenderAsync() with a dict…
AJCJ1 Nov 29, 2024
6cf632a
chore(repo): rename urlgenerator to renderLinkGenerator
AJCJ1 Nov 29, 2024
97ae1d6
chore(repo): change core proj to urlboxSDK to match namespace
AJCJ1 Nov 29, 2024
1fdf27a
fix(repo): rename target project for tests
AJCJ1 Nov 29, 2024
d302acc
chore(repo): move webhook tests to own file
AJCJ1 Nov 29, 2024
5c7ea32
feat(repo): add tests for getstatus
AJCJ1 Nov 29, 2024
c078e1c
chore(repo): reorganise project to not use redundant files/folders, s…
AJCJ1 Dec 2, 2024
2a0d662
chore(repo): remove unused nested urlbox dir
AJCJ1 Dec 2, 2024
4faf23a
feat(repo): add an extension method for DI easy injectionwhich takes …
AJCJ1 Dec 3, 2024
b9b552c
chore(repo): split error into its own response file
AJCJ1 Dec 3, 2024
8aacd7d
refactor(repo): remove redundant webhookerror for generic UrlboxError
AJCJ1 Dec 3, 2024
a42700c
refactor(repo): place urlbox in main namespace, and make quality vali…
AJCJ1 Dec 5, 2024
716be55
chore(repo): move di test into dir
AJCJ1 Dec 11, 2024
bc5ca98
feat(repo): add a class to mock http requests with use of moq
AJCJ1 Dec 11, 2024
552c974
chore(repo): linting and adding namespaces where missing
AJCJ1 Dec 11, 2024
4992e33
feat(repo): add partial class to implement constructor for the quickt…
AJCJ1 Dec 11, 2024
451a49a
feat(repo): add internal constructor for testing, which allows mockin…
AJCJ1 Dec 11, 2024
3e45152
feat(repo): extract value validation logic for reusability
AJCJ1 Dec 11, 2024
791e92b
refactor(repo): refactor repo+tests to be compatible with new quickty…
AJCJ1 Dec 11, 2024
549403f
refactor(repo): move validation logic from builder into urlboxoptions…
AJCJ1 Dec 11, 2024
38cf96b
chore(repo): move main urlbox class to parent dir
AJCJ1 Dec 11, 2024
47f81f6
feat(repo): make render links signed by default
AJCJ1 Dec 11, 2024
c296933
chore(repo): keep usage of typing over var consistent
AJCJ1 Dec 11, 2024
5e49360
chore(repo): improve test coverage
AJCJ1 Dec 12, 2024
ad12a54
refactor(repo): remove custom types now that quicktype strictly turns…
AJCJ1 Dec 13, 2024
a1e6fb9
chore(repo): test update with ssl
AJCJ1 Dec 13, 2024
d6d88dd
feat(repo): update the readme with the new DI instructions
AJCJ1 Dec 13, 2024
4ba93f8
fix(repo): move private methods in urlbox to bottom of file, add over…
AJCJ1 Dec 16, 2024
0b3e8a1
refactor(repo): prune readme to make shorter, validate examples
AJCJ1 Dec 16, 2024
1d98433
feat(repo): add example for fail on options
AJCJ1 Dec 16, 2024
73a0732
chore(repo): ensure images show in readme, add example outputs
AJCJ1 Dec 16, 2024
87c3d46
chore(repo): add examples to TOC
AJCJ1 Dec 16, 2024
97a02c1
chore(repo): remove full page and mobile helper methods
AJCJ1 Dec 16, 2024
9468887
chore(repo): add examples for full page and mobile
AJCJ1 Dec 16, 2024
6a8dedf
feat(repo): implement extraction methods for side formats
AJCJ1 Dec 17, 2024
0d0ce2a
feat(repo): update readme with extraction methods and how to use IDic…
AJCJ1 Dec 17, 2024
39b173b
feat(repo): add trivial change to test actions
AJCJ1 Dec 18, 2024
d170eb2
fix(repo): add missing awaits to tests
AJCJ1 Dec 18, 2024
dfc0573
fix(repo): ensure nullable fields dont show up in render response
AJCJ1 Dec 18, 2024
7c1d817
feat(repo): ensure user agent is added to each request
AJCJ1 Dec 18, 2024
9756e2a
feat(repo): add gh test running workflow
AJCJ1 Dec 20, 2024
4e8345d
chore(repo): remove tmp comment line
AJCJ1 Dec 20, 2024
f821f66
feat(repo): add in a new project showing example and webhooks
AJCJ1 Dec 20, 2024
156c009
fix(repo): ensure null values dont show in responses
AJCJ1 Dec 20, 2024
9ca7d7d
refactor(readme): update readme to reflect new example webhook project
AJCJ1 Dec 20, 2024
ec2b65d
chore(repo): remove format arg from render links as can just use opti…
AJCJ1 Dec 20, 2024
9fba503
fix(repo): fix failing test
AJCJ1 Dec 20, 2024
8994f20
feat(repo): add deployment workflow less deploy step
AJCJ1 Dec 20, 2024
f85de37
feat(repo): add in push to nuget
AJCJ1 Dec 23, 2024
5e6735c
chore(repo): change on push syntax to match other repos
AJCJ1 Dec 23, 2024
ee10476
fix(repo): ensure response type edge cases covereD
AJCJ1 Dec 24, 2024
fc81fa9
fix(repo): account for edge case in response type for POST methods
AJCJ1 Dec 24, 2024
c7f6d04
refactor(repo): add tests to deploy workflow
AJCJ1 Dec 24, 2024
3ed6603
feat(repo): creates auto release with conventional commits
AJCJ1 Dec 24, 2024
0d55c0e
chore(repo): rename examples file to assets
AJCJ1 Dec 24, 2024
a05536e
chore(repo): replace mp4 with full page scrolling mp4 example, update…
AJCJ1 Dec 24, 2024
1ec7023
chore(repo): remove di test as already covered in extension tests
AJCJ1 Dec 27, 2024
8ed5634
refactor(repo): repo uses config obj to extract configuration logic f…
AJCJ1 Dec 27, 2024
d2e94cc
chore(repo): cleanup
AJCJ1 Dec 27, 2024
3ef1481
refactor(repo): remove throw option from config
AJCJ1 Dec 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/deploy_to_nuget.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Deploy to NuGet

on:
workflow_dispatch:
pull_request:
types: [ closed ]

jobs:
deploy:
if: github.event.pull_request.merged == true
name: Deploy to NuGet
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [6.0]
steps:
# Checkout the code
- name: Checkout code
uses: actions/checkout@v4

# Set up .NET
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
cache: true
cache-dependency-path: "./UrlboxSDK"

# Restore dependencies
- name: Restore dependencies
run: dotnet restore ./UrlboxSDK

# Run tests
- name: Run tests
run: dotnet test --no-restore

# Build the project
- name: Build the project
run: dotnet build ./UrlboxSDK --configuration Release --no-restore

# Pack the NuGet package somewhere unique
- name: Pack NuGet package
run: dotnet pack --configuration Release --no-build --output ./package

# Push the NuGet package to NuGet.org
- name: Publish to NuGet
run: dotnet nuget push "./package/*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json

# https://github.com/googleapis/release-please/blob/72b0ab360c3d6635397e8b02f4d3f9f53932e23c/docs/customizing.md
- name: Create Release
uses: google-github-actions/release-please-action@v4
with:
release-type: simple

28 changes: 28 additions & 0 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: run tests

on:
push:

jobs:
test:
permissions:
contents: read
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [6.0]
steps:
- name: Checkout urlbox-dotnet
uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
cache: true
cache-dependency-path: "./UrlboxSDK"

- name: Install dependencies
run: dotnet restore

- name: Run tests
run: dotnet test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ Thumbs.db

# dotCover
*.dotCover

.idea
Binary file added Assets/highlight.pdf
Binary file not shown.
35 changes: 35 additions & 0 deletions Assets/html.html

Large diffs are not rendered by default.

Binary file added Assets/javascript.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Assets/mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Assets/mp4.mp4
Binary file not shown.
Binary file added Assets/pdf.pdf
Binary file not shown.
13 changes: 13 additions & 0 deletions Example/Example.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../UrlboxSDK/UrlboxSDK.csproj" />
</ItemGroup>

</Project>
95 changes: 95 additions & 0 deletions Example/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using UrlboxSDK.DI.Extension;
using UrlboxSDK;
using UrlboxSDK.Response.Resource;
using UrlboxSDK.Exception;
using UrlboxSDK.Options.Resource;
using UrlboxSDK.Webhook.Resource;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);

// Add Urlbox to the service container
builder.Services.AddUrlbox(config =>
{
// TODO Replace these with your keys from the dashboard: https://urlbox.com/dashboard/api
config.Key = "YOUR PUBLISHABLE API KEY HERE";
config.Secret = "YOUR SECRET KEY HERE";
// TODO optionally add this in to test out webhooks
config.WebhookSecret = "YOUR WEBHOOK SECRET KEY HERE";
// if you need to use one of our specific subdomains
// config.BaseUrl = "https://api-eu.urlbox.com";
});

var app = builder.Build();

// Urlbox gets injected by the service container
app.MapGet("/", async (HttpContext context, IUrlbox urlbox) =>
{
try
{
// Use the static .Options() method to choose your options
UrlboxOptions options = Urlbox.Options(url: "https://urlbox.com/docs")
// Play around with various options here
.Format(Format.Jpeg)
// Want to test out webhooks? see the POST endpoint below
// .WebhookUrl("https://YOUR NGROK FORWARDING ENDPOINT/webhook/urlbox")
.Build();

// Runs an async render, polls for success
// AsyncUrlboxResponse takeScreenshotResponse = await urlbox.TakeScreenshot(options);

// Runs an async render, gives status response
AsyncUrlboxResponse renderAsyncResponse = await urlbox.RenderAsync(options);

// Runs an sync render, waits for success before returning
// SyncUrlboxResponse renderSyncResponse = await urlbox.Render(options);

return Results.Json(new
{
message = "Screenshot generated!",
// ResponseFromTakeScreenshot = takeScreenshotResponse,
ResponseFromRenderAsync = renderAsyncResponse,
// ResponseFromRenderSync = renderSyncResponse
});
}
// Want to test how the exception looks? try this as the url in options: "https://notresolvableurlbox.com"
catch (UrlboxException ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.RequestId);
return Results.Json(new { message = "Failed to generate screenshot, urlbox exception", error = ex.Message, reqId = ex.RequestId });
}
});

/*
Webhook Example:

1. Make sure you've set your webhook secret in your Urlbox instantiation (line 16)
2. Get ngrok (make an account and install on your computer) https://ngrok.com/
3. Run this project with `dotnet run`.
4. Using ngrok expose the localhost port the .net server runs on EG for 5096 `ngrok http 5096`
4. Take the ngrok forwarding address shown in CLI EG https://2c85-80-41-190-113.ngrok-free.app and
replace the above .WebhookUrl() arg with it in your options, including the /webhook/urlbox endpoint.
5. Make a request to the GET endpoint "/" above with one of the render methods, and Urlbox will make a POST to your ngrok endpoint /webhook/urlbox.
EG: curl -i http://localhost:5096
*/
app.MapPost("/webhook/urlbox", async (HttpContext context, IUrlbox urlbox) =>
{
using StreamReader stream = new StreamReader(context.Request.Body);

if (!context.Request.Headers.TryGetValue("x-urlbox-signature", out var headerValue))
{
throw new Exception("Header 'x-urlbox-signature' not found.");
}

UrlboxWebhookResponse verifiedResponse = urlbox.VerifyWebhookSignature(headerValue.ToString(), await stream.ReadToEndAsync());

string json = JsonSerializer.Serialize(verifiedResponse, new JsonSerializerOptions
{
WriteIndented = true
});

Console.WriteLine(json);
});

app.Run();
38 changes: 38 additions & 0 deletions Example/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:52163",
"sslPort": 44353
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5096",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:7078;http://localhost:5096",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
8 changes: 8 additions & 0 deletions Example/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
9 changes: 9 additions & 0 deletions Example/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Binary file added Images/gh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/projectKeys.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/urlbox-graphic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Images/urlbox-png.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading